2 MAPI Proxy - Exchange NSPI Server
6 Copyright (C) Julien Kerihuel 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 \file dcesrv_exchange_nsp.c
25 \brief OpenChange NSPI Server implementation
28 #include "mapiproxy/dcesrv_mapiproxy.h"
29 #include "mapiproxy/libmapiproxy.h"
30 #include "dcesrv_exchange_nsp.h"
32 struct exchange_nsp_session *nsp_session = NULL;
35 \details exchange_nsp NspiBind (0x0) function, Initiates a NSPI
36 session with the client.
38 This function checks if the user is an Exchange user and input
39 parameters like codepage are valid. If it passes the tests, the
40 function initializes an emsabp context and returns to the client a
41 valid policy_handle and expected reply parameters.
43 \param dce_call pointer to the session context
44 \param mem_ctx pointer to the memory context
45 \param r pointer to the NspiBind call structure
47 \return MAPI_E_SUCCESS on success, otherwise a MAPI error
49 static enum MAPISTATUS dcesrv_NspiBind(struct dcesrv_call_state *dce_call,
53 struct GUID *guid = (struct GUID *) NULL;
54 struct emsabp_context *emsabp_ctx;
55 struct dcesrv_handle *handle;
56 struct policy_handle wire_handle;
57 struct exchange_nsp_session *session;
59 DEBUG(5, ("exchange_nsp: NspiBind (0x0)\n"));
61 /* Step 0. Ensure incoming user is authenticated */
62 if (!NTLM_AUTH_IS_OK(dce_call)) {
63 DEBUG(1, ("No challenge requested by client, cannot authenticate\n"));
65 wire_handle.handle_type = EXCHANGE_HANDLE_NSP;
66 wire_handle.uuid = GUID_zero();
67 *r->out.handle = wire_handle;
69 r->out.mapiuid = r->in.mapiuid;
70 r->out.result = MAPI_E_LOGON_FAILED;
71 return MAPI_E_LOGON_FAILED;
74 /* Step 1. Initialize the emsabp context */
75 emsabp_ctx = emsabp_init(dce_call->conn->dce_ctx->lp_ctx);
76 OPENCHANGE_RETVAL_IF(!emsabp_ctx, MAPI_E_FAILONEPROVIDER, NULL);
78 /* Step 2. Check if incoming user belongs to the Exchange organization */
79 if (emsabp_verify_user(dce_call, emsabp_ctx) == false) {
80 talloc_free(emsabp_ctx);
82 wire_handle.handle_type = EXCHANGE_HANDLE_NSP;
83 wire_handle.uuid = GUID_zero();
84 *r->out.handle = wire_handle;
86 r->out.mapiuid = r->in.mapiuid;
87 r->out.result = MAPI_E_LOGON_FAILED;
88 return MAPI_E_LOGON_FAILED;
91 /* Step 3. Check if valid cpID has been supplied */
92 if (emsabp_verify_codepage(dce_call->conn->dce_ctx->lp_ctx,
93 emsabp_ctx, r->in.pStat->CodePage) == false) {
94 talloc_free(emsabp_ctx);
96 wire_handle.handle_type = EXCHANGE_HANDLE_NSP;
97 wire_handle.uuid = GUID_zero();
98 *r->out.handle = wire_handle;
100 r->out.mapiuid = r->in.mapiuid;
101 r->out.result = MAPI_E_UNKNOWN_CPID;
102 return MAPI_E_UNKNOWN_CPID;
105 /* Step 4. Retrieve OpenChange server GUID */
106 guid = emsabp_get_server_GUID(dce_call->conn->dce_ctx->lp_ctx, emsabp_ctx);
107 OPENCHANGE_RETVAL_IF(!guid, MAPI_E_FAILONEPROVIDER, emsabp_ctx);
109 /* Step 5. Fill NspiBind reply */
110 handle = dcesrv_handle_new(dce_call->context, EXCHANGE_HANDLE_NSP);
111 OPENCHANGE_RETVAL_IF(!handle, MAPI_E_NOT_ENOUGH_RESOURCES, emsabp_ctx);
113 handle->data = (void *) emsabp_ctx;
114 *r->out.handle = handle->wire_handle;
115 r->out.mapiuid = guid;
116 r->out.result = MAPI_E_SUCCESS;
118 /* Step 6. Associate this emsabp context to the session */
119 session = talloc((TALLOC_CTX *)nsp_session, struct exchange_nsp_session);
120 OPENCHANGE_RETVAL_IF(!session, MAPI_E_NOT_ENOUGH_RESOURCES, emsabp_ctx);
122 session->session = mpm_session_init((TALLOC_CTX *)nsp_session, dce_call);
123 OPENCHANGE_RETVAL_IF(!session->session, MAPI_E_NOT_ENOUGH_RESOURCES, emsabp_ctx);
125 mpm_session_set_private_data(session->session, (void *) emsabp_ctx);
126 mpm_session_set_destructor(session->session, emsabp_destructor);
128 DLIST_ADD_END(nsp_session, session, struct exchange_nsp_session *);
130 return MAPI_E_SUCCESS;
135 \details exchange_nsp NspiUnbind (0x1) function, Terminates a NSPI
136 session with the client
138 \param dce_call pointer to the session context
139 \param mem_ctx pointer to the memory context
140 \param r pointer to the NspiUnbind call structure
142 static enum MAPISTATUS dcesrv_NspiUnbind(struct dcesrv_call_state *dce_call,
144 struct NspiUnbind *r)
146 struct dcesrv_handle *h;
147 struct exchange_nsp_session *session;
149 DEBUG(5, ("exchange_nsp: NspiUnbind (0x1)\n"));
151 /* Step 0. Ensure incoming user is authenticated */
152 if (!NTLM_AUTH_IS_OK(dce_call)) {
153 DEBUG(1, ("No challenge requested by client, cannot authenticate\n"));
154 return MAPI_E_LOGON_FAILED;
157 /* Step 1. Retrieve handle and free if emsabp context and session are available */
158 h = dcesrv_handle_fetch(dce_call->context, r->in.handle, DCESRV_HANDLE_ANY);
160 for (session = nsp_session; session; session = session->next) {
161 if ((mpm_session_cmp(session->session, dce_call) == true)) {
162 mpm_session_release(session->session);
163 DLIST_REMOVE(nsp_session, session);
164 DEBUG(6, ("[%s:%d]: Session found and released\n", __FUNCTION__, __LINE__));
171 return MAPI_E_SUCCESS;
176 \details exchange_nsp NspiUpdateStat (0x2) function
178 \param dce_call pointer to the session context
179 \param mem_ctx pointer to the memory context
180 \param r pointer to the NspiUpdateStat request data
182 \return MAPI_E_SUCCESS on success
184 static enum MAPISTATUS dcesrv_NspiUpdateStat(struct dcesrv_call_state *dce_call,
186 struct NspiUpdateStat *r)
188 DEBUG(3, ("exchange_nsp: NspiUpdateStat (0x2) not implemented\n"));
189 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
194 \details exchange_nsp NspiQueryRows (0x3) function
196 \param dce_call pointer to the session context
197 \param mem_ctx pointer to the memory context
198 \param r pointer to the NspiQueryRows request data
200 \return MAPI_E_SUCCESS on success
202 static enum MAPISTATUS dcesrv_NspiQueryRows(struct dcesrv_call_state *dce_call,
204 struct NspiQueryRows *r)
206 DEBUG(3, ("exchange_nsp: NspiQueryRows (0x3) not implemented\n"));
207 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
212 \details exchange_nsp NspiSeekEntries (0x4) function
214 \param dce_call pointer to the session context
215 \param mem_ctx pointer to the memory context
216 \param r pointer to the NspiSeekEntries request data
218 \return MAPI_E_SUCCESS on success
220 static enum MAPISTATUS dcesrv_NspiSeekEntries(struct dcesrv_call_state *dce_call,
222 struct NspiSeekEntries *r)
224 DEBUG(3, ("exchange_nsp: NspiSeekEntries (0x4) not implemented\n"));
225 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
230 \details exchange_nsp NspiGetMatches (0x5) function
232 \param dce_call pointer to the session context
233 \param mem_ctx pointer to the memory context
234 \param r pointer to the NspiGetMatches request data
236 \return MAPI_E_SUCCESS on success
238 static enum MAPISTATUS dcesrv_NspiGetMatches(struct dcesrv_call_state *dce_call,
240 struct NspiGetMatches *r)
242 DEBUG(3, ("exchange_nsp: NspiGetMatches (0x5) not implemented\n"));
243 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
248 \details exchange_nsp NspiResortRestriction (0x6) function
250 \param dce_call pointer to the session context
251 \param mem_ctx pointer to the memory context
252 \param r pointer to the NspiResortRestriction request data
254 \return MAPI_E_SUCCESS on success
256 static enum MAPISTATUS dcesrv_NspiResortRestriction(struct dcesrv_call_state *dce_call,
258 struct NspiResortRestriction *r)
260 DEBUG(3, ("exchange_nsp: NspiResortRestriction (0x6) not implemented\n"));
261 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
266 \details exchange_nsp NspiDNToMId (0x7) function
268 \param dce_call pointer to the session context
269 \param mem_ctx pointer to the memory context
270 \param r pointer to the NspiDNToMId request data
272 \return MAPI_E_SUCCESS on success
274 static enum MAPISTATUS dcesrv_NspiDNToMId(struct dcesrv_call_state *dce_call,
276 struct NspiDNToMId *r)
278 DEBUG(3, ("exchange_nsp: NspiDNToMId (0x7) not implemented\n"));
279 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
284 \details exchange_nsp NspiGetPropList (0x8) function
286 \param dce_call pointer to the session context
287 \param mem_ctx pointer to the memory context
288 \param r pointer to the NspiGetPropList request data
290 \return MAPI_E_SUCCESS on success
292 static enum MAPISTATUS dcesrv_NspiGetPropList(struct dcesrv_call_state *dce_call,
294 struct NspiGetPropList *r)
296 DEBUG(3, ("exchange_nsp: NspiGetPropList (0x8) not implemented\n"));
297 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
302 \details exchange_nsp NspiGetProps (0x9) function
304 \param dce_call pointer to the session context
305 \param mem_ctx pointer to the memory context
306 \param r pointer to the NspiGetProps request data
308 \return MAPI_E_SUCCESS on success
310 static enum MAPISTATUS dcesrv_NspiGetProps(struct dcesrv_call_state *dce_call,
312 struct NspiGetProps *r)
314 DEBUG(3, ("exchange_nsp: NspiGetProps (0x9) not implemented\n"));
315 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
320 \details exchange_nsp NspiCompareMIds (0xA) function
322 \param dce_call pointer to the session context
323 \param mem_ctx pointer to the memory context
324 \param r pointer to the NspiCompareMIds request data
326 \return MAPI_E_SUCCESS on success
328 static enum MAPISTATUS dcesrv_NspiCompareMIds(struct dcesrv_call_state *dce_call,
330 struct NspiCompareMIds *r)
332 DEBUG(3, ("exchange_nsp: NspiCompareMIds (0xA) not implemented\n"));
333 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
338 \details exchange_nsp NspiModProps (0xB) function
340 \param dce_call pointer to the session context
341 \param mem_ctx pointer to the memory context
342 \param r pointer to the NspiModProps request data
344 \return MAPI_E_SUCCESS on success
347 static enum MAPISTATUS dcesrv_NspiModProps(struct dcesrv_call_state *dce_call,
349 struct NspiModProps *r)
351 DEBUG(3, ("exchange_nsp: NspiModProps (0xB) not implemented\n"));
352 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
357 \details exchange_nsp NspiGetSpecialTable (0xC) function
359 \param dce_call pointer to the session context
360 \param mem_ctx pointer to the memory context
361 \param r pointer to the NspiGetSpecialTable request data
363 \return MAPI_E_SUCCESS on success
366 static enum MAPISTATUS dcesrv_NspiGetSpecialTable(struct dcesrv_call_state *dce_call,
368 struct NspiGetSpecialTable *r)
370 DEBUG(3, ("exchange_nsp: NspiGetSpecialTable (0xC) not implemented\n"));
371 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
376 \details exchange_nsp NspiGetTemplateInfo (0xD) function
378 \param dce_call pointer to the session context
379 \param mem_ctx pointer to the memory context
380 \param r pointer to the NspiGetTemplateInfo request data
382 \return MAPI_E_SUCCESS on success
385 static enum MAPISTATUS dcesrv_NspiGetTemplateInfo(struct dcesrv_call_state *dce_call,
387 struct NspiGetTemplateInfo *r)
389 DEBUG(3, ("exchange_nsp: NspiGetTemplateInfo (0xD) not implemented\n"));
390 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
395 \details exchange_nsp NspiModLinkAtt (0xE) function
397 \param dce_call pointer to the session context
398 \param mem_ctx pointer to the memory context
399 \param r pointer to the NspiModLinkAtt request data
401 \return MAPI_E_SUCCESS on success
404 static enum MAPISTATUS dcesrv_NspiModLinkAtt(struct dcesrv_call_state *dce_call,
406 struct NspiModLinkAtt *r)
408 DEBUG(3, ("exchange_nsp: NspiModLinkAtt (0xE) not implemented\n"));
409 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
414 \details exchange_nsp NspiDeleteEntries (0xF) function
416 \param dce_call pointer to the session context
417 \param mem_ctx pointer to the memory context
418 \param r pointer to the NspiDeleteEntries request data
420 \return MAPI_E_SUCCESS on success
423 static enum MAPISTATUS dcesrv_NspiDeleteEntries(struct dcesrv_call_state *dce_call,
425 struct NspiDeleteEntries *r)
427 DEBUG(3, ("exchange_nsp: NspiDeleteEntries (0xF) not implemented\n"));
428 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
433 \details exchange_nsp NspiQueryColumns (0x10) function
435 \param dce_call pointer to the session context
436 \param mem_ctx pointer to the memory context
437 \param r pointer to the NspiQueryColumns request data
439 \return MAPI_E_SUCCESS on success
442 static enum MAPISTATUS dcesrv_NspiQueryColumns(struct dcesrv_call_state *dce_call,
444 struct NspiQueryColumns *r)
446 DEBUG(3, ("exchange_nsp: NspiQueryColumns (0x10) not implemented\n"));
447 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
452 \details exchange_nsp NspiGetNamesFromIDs (0x11) function
454 \param dce_call pointer to the session context
455 \param mem_ctx pointer to the memory context
456 \param r pointer to the NspiGetNamesFromIDs request data
458 \return MAPI_E_SUCCESS on success
461 static enum MAPISTATUS dcesrv_NspiGetNamesFromIDs(struct dcesrv_call_state *dce_call,
463 struct NspiGetNamesFromIDs *r)
465 DEBUG(3, ("exchange_nsp: NspiGetNamesFromIDs (0x11) not implemented\n"));
466 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
471 \details exchange_nsp NspiGetIDsFromNames (0x12) function
473 \param dce_call pointer to the session context
474 \param mem_ctx pointer to the memory context
475 \param r pointer to the NspiGetIDsFromNames request data
477 \return MAPI_E_SUCCESS on success
480 static enum MAPISTATUS dcesrv_NspiGetIDsFromNames(struct dcesrv_call_state *dce_call,
482 struct NspiGetIDsFromNames *r)
484 DEBUG(3, ("exchange_nsp: NspiGetIDsFromNames (0x12) not implemented\n"));
485 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
490 \details exchange_nsp NspiResolveNames (0x13) function
492 \param dce_call pointer to the session context
493 \param mem_ctx pointer to the memory context
494 \param r pointer to the NspiResolveNames request data
496 \return MAPI_E_SUCCESS on success
499 static enum MAPISTATUS dcesrv_NspiResolveNames(struct dcesrv_call_state *dce_call,
501 struct NspiResolveNames *r)
503 DEBUG(3, ("exchange_nsp: NspiResolveNames (0x13) not implemented\n"));
504 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
509 \details exchange_nsp NspiResolveNamesW (0x14) function
511 \param dce_call pointer to the session context
512 \param mem_ctx pointer to the memory context
513 \param r pointer to the NspiResolveNamesW request data
515 \return MAPI_E_SUCCESS on success
518 static enum MAPISTATUS dcesrv_NspiResolveNamesW(struct dcesrv_call_state *dce_call,
520 struct NspiResolveNamesW *r)
522 DEBUG(3, ("exchange_nsp: NspiResolveNamesW (0x14) not implemented\n"));
523 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
528 \details Dispatch incoming NSPI call to the correct OpenChange
531 \param dce_call pointer to the session context
532 \param mem_ctx pointer to the memory context
533 \param r generic pointer on NSPI data
534 \param mapiproxy pointer to the mapiproxy structure controlling
539 static NTSTATUS dcesrv_exchange_nsp_dispatch(struct dcesrv_call_state *dce_call,
541 void *r, struct mapiproxy *mapiproxy)
543 enum MAPISTATUS retval;
544 const struct ndr_interface_table *table;
547 table = (const struct ndr_interface_table *) dce_call->context->iface->private;
548 opnum = dce_call->pkt.u.request.opnum;
551 if (!table) return NT_STATUS_UNSUCCESSFUL;
552 if (table->name && strcmp(table->name, NDR_EXCHANGE_NSP_NAME)) return NT_STATUS_UNSUCCESSFUL;
556 retval = dcesrv_NspiBind(dce_call, mem_ctx, (struct NspiBind *)r);
559 retval = dcesrv_NspiUnbind(dce_call, mem_ctx, (struct NspiUnbind *)r);
561 case NDR_NSPIUPDATESTAT:
562 retval = dcesrv_NspiUpdateStat(dce_call, mem_ctx, (struct NspiUpdateStat *)r);
564 case NDR_NSPIQUERYROWS:
565 retval = dcesrv_NspiQueryRows(dce_call, mem_ctx, (struct NspiQueryRows *)r);
567 case NDR_NSPISEEKENTRIES:
568 retval = dcesrv_NspiSeekEntries(dce_call, mem_ctx, (struct NspiSeekEntries *)r);
570 case NDR_NSPIGETMATCHES:
571 retval = dcesrv_NspiGetMatches(dce_call, mem_ctx, (struct NspiGetMatches *)r);
573 case NDR_NSPIRESORTRESTRICTION:
574 retval = dcesrv_NspiResortRestriction(dce_call, mem_ctx, (struct NspiResortRestriction *)r);
576 case NDR_NSPIDNTOMID:
577 retval = dcesrv_NspiDNToMId(dce_call, mem_ctx, (struct NspiDNToMId *)r);
579 case NDR_NSPIGETPROPLIST:
580 retval = dcesrv_NspiGetPropList(dce_call, mem_ctx, (struct NspiGetPropList *)r);
582 case NDR_NSPIGETPROPS:
583 retval = dcesrv_NspiGetProps(dce_call, mem_ctx, (struct NspiGetProps *)r);
585 case NDR_NSPICOMPAREMIDS:
586 retval = dcesrv_NspiCompareMIds(dce_call, mem_ctx, (struct NspiCompareMIds *)r);
588 case NDR_NSPIMODPROPS:
589 retval = dcesrv_NspiModProps(dce_call, mem_ctx, (struct NspiModProps *)r);
591 case NDR_NSPIGETSPECIALTABLE:
592 retval = dcesrv_NspiGetSpecialTable(dce_call, mem_ctx, (struct NspiGetSpecialTable *)r);
594 case NDR_NSPIGETTEMPLATEINFO:
595 retval = dcesrv_NspiGetTemplateInfo(dce_call, mem_ctx, (struct NspiGetTemplateInfo *)r);
597 case NDR_NSPIMODLINKATT:
598 retval = dcesrv_NspiModLinkAtt(dce_call, mem_ctx, (struct NspiModLinkAtt *)r);
600 case NDR_NSPIDELETEENTRIES:
601 retval = dcesrv_NspiDeleteEntries(dce_call, mem_ctx, (struct NspiDeleteEntries *)r);
603 case NDR_NSPIQUERYCOLUMNS:
604 retval = dcesrv_NspiQueryColumns(dce_call, mem_ctx, (struct NspiQueryColumns *)r);
606 case NDR_NSPIGETNAMESFROMIDS:
607 retval = dcesrv_NspiGetNamesFromIDs(dce_call, mem_ctx, (struct NspiGetNamesFromIDs *)r);
609 case NDR_NSPIGETIDSFROMNAMES:
610 retval = dcesrv_NspiGetIDsFromNames(dce_call, mem_ctx, (struct NspiGetIDsFromNames *)r);
612 case NDR_NSPIRESOLVENAMES:
613 retval = dcesrv_NspiResolveNames(dce_call, mem_ctx, (struct NspiResolveNames *)r);
615 case NDR_NSPIRESOLVENAMESW:
616 retval = dcesrv_NspiResolveNamesW(dce_call, mem_ctx, (struct NspiResolveNamesW *)r);
625 \details Initialize the NSPI OpenChange server
627 \param dce_ctx pointer to the server context
629 \return NT_STATUS_OK on success, otherwise NT_STATUS_NO_MEMORY
631 static NTSTATUS dcesrv_exchange_nsp_init(struct dcesrv_context *dce_ctx)
633 /* Initialize exchange_nsp session */
634 nsp_session = talloc_zero(dce_ctx, struct exchange_nsp_session);
635 if (!nsp_session) return NT_STATUS_NO_MEMORY;
636 nsp_session->session = NULL;
643 \details Terminates the NSPI connection and release the associated
644 session and context if still available. This case occurs when the
645 client doesn't call NspiUnbind but quit unexpectedly.
647 \param server_id reference to the server identifier structure
648 \param context_id the connection context identifier
650 \return NT_STATUS_OK on success
652 static NTSTATUS dcesrv_exchange_nsp_unbind(struct server_id server_id, uint32_t context_id)
654 struct exchange_nsp_session *session;
656 for (session = nsp_session; session; session = session->next) {
657 if ((mpm_session_cmp_sub(session->session, server_id, context_id) == true)) {
658 mpm_session_release(session->session);
659 DLIST_REMOVE(nsp_session, session);
660 DEBUG(6, ("[%s:%d]: Session found and released\n", __FUNCTION__, __LINE__));
670 \details Entry point for the default OpenChange NSPI server
672 \return NT_STATUS_OK on success, otherwise NTSTATUS error
674 NTSTATUS samba_init_module(void)
676 struct mapiproxy_module server;
679 /* Fill in our name */
680 server.name = "exchange_nsp";
681 server.status = MAPIPROXY_DEFAULT;
682 server.description = "OpenChange NSPI server";
683 server.endpoint = "exchange_nsp";
685 /* Fill in all the operations */
686 server.init = dcesrv_exchange_nsp_init;
687 server.unbind = dcesrv_exchange_nsp_unbind;
688 server.dispatch = dcesrv_exchange_nsp_dispatch;
691 server.ndr_pull = NULL;
693 /* Register ourselves with the MAPIPROXY server subsystem */
694 ret = mapiproxy_server_register(&server);
695 if (!NT_STATUS_IS_OK(ret)) {
696 DEBUG(0, ("Failed to register the 'exchange_nsp' default mapiproxy server!\n"));