Release 980517
[wine] / misc / network.c
1 /*
2  * Network functions
3  *
4  * This is the MPR.DLL stuff from Win32,  as well as the USER
5  * stuff by the same names in Win 3.x.  
6  *
7  */
8
9 #include <ctype.h>
10 #include <stdio.h>
11
12 #include "windows.h"
13 #include "winerror.h"
14 #include "drive.h"
15 #include "wnet.h"
16 #include "debug.h"
17 #include "win.h"
18
19 /********************************************************************
20  *  WNetAddConnection16 [USER.517]  Directs a local device to net
21  * 
22  * Redirects a local device (either a disk drive or printer port)
23  * to a shared device on a remote server.
24  */
25 UINT16 WINAPI WNetAddConnection16(LPSTR lpNetPath, LPSTR lpPassWord,
26                                 LPSTR lpLocalName)
27 {       
28    return WNetAddConnection32A(lpNetPath, lpPassWord, lpLocalName);
29 }
30
31 /* [MPR.50] */
32
33 UINT32 WNetAddConnection32A(LPSTR NetPath, LPSTR PassWord,
34                             LPSTR LocalName)
35 {
36    FIXME(wnet, "('%s', %p, '%s'): stub\n",
37          NetPath, PassWord, LocalName);
38    return WN_NO_NETWORK;
39 }
40
41 /* [MPR.51] */
42
43 UINT32 WNetAddConnection32W(LPWSTR NetPath, 
44                             LPWSTR PassWord,
45                             LPWSTR LocalName)
46 {
47    FIXME(wnet, " stub!\n");
48    return WN_NO_NETWORK;
49 }
50
51 /* **************************************************************** 
52  * WNetAddConnection2_32A [MPR.46] 
53  */
54
55 UINT32
56 WNetAddConnection2_32A(LPNETRESOURCE32A netresource, /* [in] */
57                        LPCSTR password,        /* [in] */     
58                        LPCSTR username,        /* [in] */
59                        DWORD flags             /* [in] */  )
60 {
61    FIXME(wnet, "(%p,%s,%s,0x%08lx), stub!\n", netresource,
62          password, username, (unsigned long) flags);
63    SetLastError(WN_NO_NETWORK);
64    return WN_NO_NETWORK;
65 }
66
67 /* ****************************************************************
68  * WNetAddConnection2W [MPR.47]
69  */
70
71 UINT32
72 WNetAddConnection2_32W(LPNETRESOURCE32W netresource, /* [in] */
73                        LPCWSTR password,        /* [in] */     
74                        LPCWSTR username,        /* [in] */
75                        DWORD flags              /* [in] */  )
76 {
77    FIXME(wnet, ", stub!\n");
78    SetLastError(WN_NO_NETWORK);
79    return WN_NO_NETWORK;
80 }
81
82 /* ****************************************************************
83  * WNetAddConnection3_32A [MPR.48]
84  */
85
86 UINT32 WNetAddConnection3_32A(HWND32 owner,
87                       LPNETRESOURCE32A netresource,
88                       LPCSTR password,
89                       LPCSTR username,
90                       DWORD flags)
91 {
92    TRACE(wnet, "owner = 0x%x\n", owner);
93    return WNetAddConnection2_32A(netresource, 
94                                  password, username, flags);
95 }
96
97 /* ****************************************************************
98  * WNetAddConnection3W [MPR.49]
99  */
100
101 UINT32 WNetAddConnection3_32W(HWND32 owner,
102                               LPNETRESOURCE32W netresource,
103                               LPCWSTR username,
104                               LPCWSTR password,
105                               DWORD flags)
106 {
107    TRACE(wnet,"owner = 0x%x\n", owner);
108    return WNetAddConnection2_32W(netresource, username, password,
109                                  flags); 
110
111
112
113 /********************************************************************
114  *   WNetCancelConnection       [USER.518]  undirects a local device
115  */
116 UINT16 WINAPI WNetCancelConnection(LPSTR lpName, BOOL16 bForce)
117 {
118     FIXME(wnet, "('%s', %04X): stub\n", lpName, bForce);
119     return WN_NO_NETWORK;
120 }
121
122
123 /**************************************************************************
124  *              WNetErrorText16       [USER.499]
125  */
126 int WINAPI WNetErrorText(WORD nError,LPSTR lpszText,WORD cbText)
127 {
128         FIXME(wnet, "(%x,%p,%x): stub\n",nError,lpszText,cbText);
129         return FALSE;
130 }
131
132 /**************************************************************************
133  *              WNetOpenJob16       [USER.501]
134  */
135 int WINAPI WNetOpenJob(LPSTR szQueue,LPSTR szJobTitle,WORD nCopies,LPWORD pfh)
136 {
137         FIXME(wnet, "('%s','%s',%x,%p): stub\n",
138               szQueue,szJobTitle,nCopies,pfh);
139         return WN_NET_ERROR;
140 }
141
142 /**************************************************************************
143  *              WNetCloseJob       [USER.502]
144  */
145 int WINAPI WNetCloseJob(WORD fh,LPWORD pidJob,LPSTR szQueue)
146 {
147         FIXME(wnet, "(%x,%p,'%s'): stub\n",fh,pidJob,szQueue);
148         return WN_NET_ERROR;
149 }
150
151 /**************************************************************************
152  *              WNetAbortJob       [USER.503]
153  */
154 int WINAPI WNetAbortJob(LPSTR szQueue,WORD wJobId)
155 {
156         FIXME(wnet, "('%s',%x): stub\n",szQueue,wJobId);
157         return WN_NET_ERROR;
158 }
159
160 /**************************************************************************
161  *              WNetHoldJob       [USER.504]
162  */
163 int WINAPI WNetHoldJob(LPSTR szQueue,WORD wJobId)
164 {
165         FIXME(wnet, "('%s',%x): stub\n",szQueue,wJobId);
166         return WN_NET_ERROR;
167 }
168
169 /**************************************************************************
170  *              WNetReleaseJob       [USER.505]
171  */
172 int WINAPI WNetReleaseJob(LPSTR szQueue,WORD wJobId)
173 {
174         FIXME(wnet, "('%s',%x): stub\n",szQueue,wJobId);
175         return WN_NET_ERROR;
176 }
177
178 /**************************************************************************
179  *              WNetCancelJob       [USER.506]
180  */
181 int WINAPI WNetCancelJob(LPSTR szQueue,WORD wJobId)
182 {
183         FIXME(wnet, "('%s',%x): stub\n",szQueue,wJobId);
184         return WN_NET_ERROR;
185 }
186
187 /**************************************************************************
188  *              WNetSetJobCopies       [USER.507]
189  */
190 int WINAPI WNetSetJobCopies(LPSTR szQueue,WORD wJobId,WORD nCopies)
191 {
192         FIXME(wnet, "('%s',%x,%x): stub\n",szQueue,wJobId,nCopies);
193         return WN_NET_ERROR;
194 }
195
196 /**************************************************************************
197  *              WNetWatchQueue       [USER.508]
198  */
199 int WINAPI WNetWatchQueue(HWND16 hWnd,LPSTR szLocal,LPSTR szUser,WORD nQueue)
200 {
201         FIXME(wnet, "(%04x,'%s','%s',%x): stub\n",hWnd,szLocal,szUser,nQueue);
202         return WN_NET_ERROR;
203 }
204
205 /**************************************************************************
206  *              WNetUnwatchQueue       [USER.509]
207  */
208 int WINAPI WNetUnwatchQueue(LPSTR szQueue)
209 {
210         FIXME(wnet, "('%s'): stub\n", szQueue);
211         return WN_NET_ERROR;
212 }
213
214 /**************************************************************************
215  *              WNetLockQueueData       [USER.510]
216  */
217 int WINAPI WNetLockQueueData(LPSTR szQueue,LPSTR szUser,void *lplpQueueStruct)
218 {
219         FIXME(wnet, "('%s','%s',%p): stub\n",szQueue,szUser,lplpQueueStruct);
220         return WN_NET_ERROR;
221 }
222
223 /**************************************************************************
224  *              WNetUnlockQueueData       [USER.511]
225  */
226 int WINAPI WNetUnlockQueueData(LPSTR szQueue)
227 {
228         FIXME(wnet, "('%s'): stub\n",szQueue);
229         return WN_NET_ERROR;
230 }
231
232
233 /********************************************************************
234  * WNetGetConnection16 [USER.512] reverse-resolves a local device
235  *
236  * RETURNS
237  * - WN_BAD_LOCALNAME     lpLocalName makes no sense
238  * - WN_NOT_CONNECTED     drive is a local drive
239  * - WN_MORE_DATA         buffer isn't big enough
240  * - WN_SUCCESS           success (net path in buffer)  
241  */
242 int WINAPI WNetGetConnection16(LPCSTR lpLocalName, 
243                                LPSTR lpRemoteName, UINT16 *cbRemoteName)
244 {
245     const char *path;
246
247     if (lpLocalName[1] == ':')
248     {
249         int drive = toupper(lpLocalName[0]) - 'A';
250         switch(GetDriveType16(drive))
251         {
252         case DRIVE_CANNOTDETERMINE:
253         case DRIVE_DOESNOTEXIST:
254             return WN_BAD_LOCALNAME;
255         case DRIVE_REMOVABLE:
256         case DRIVE_FIXED:
257             return WN_NOT_CONNECTED;
258         case DRIVE_REMOTE:
259             path = DRIVE_GetLabel(drive);
260             if (strlen(path) + 1 > *cbRemoteName)
261             {
262                 *cbRemoteName = strlen(path) + 1;
263                 return WN_MORE_DATA;
264             }
265             strcpy( lpRemoteName, path );
266             *cbRemoteName = strlen(lpRemoteName) + 1;
267             return WN_SUCCESS;
268         }
269     }
270     return WN_BAD_LOCALNAME;
271 }
272
273 /**************************************************************************
274  *                              WNetGetConnectionA      [MPR.70]
275  */
276 DWORD WINAPI
277 WNetGetConnection32A(LPCSTR localname,LPSTR remotename,LPDWORD buflen)
278 {
279         UINT16  x;
280         DWORD   ret = WNetGetConnection16(localname,remotename,&x);
281         *buflen = x;
282         return ret;
283 }
284
285
286 /**************************************************************************
287  *                              WNetGetCaps             [USER.513]
288  */
289 int WINAPI WNetGetCaps(WORD capability)
290 {
291         switch (capability) {
292                 case WNNC_SPEC_VERSION:
293                 {
294                         return 0x30a; /* WfW 3.11(and apparently other 3.1x) */
295                 }
296                 case WNNC_NET_TYPE:
297                 /* hi byte = network type, 
298                    lo byte = network vendor (Netware = 0x03) [15 types] */
299                 return WNNC_NET_MultiNet | WNNC_SUBNET_WinWorkgroups;
300
301                 case WNNC_DRIVER_VERSION:
302                 /* driver version of vendor */
303                 return 0x100; /* WfW 3.11 */
304
305                 case WNNC_USER:
306                 /* 1 = WNetGetUser is supported */
307                 return 1;
308
309                 case WNNC_CONNECTION:
310                 /* returns mask of the supported connection functions */
311                 return  WNNC_CON_AddConnection|WNNC_CON_CancelConnection
312                         |WNNC_CON_GetConnections/*|WNNC_CON_AutoConnect*/
313                         |WNNC_CON_BrowseDialog|WNNC_CON_RestoreConnection;
314
315                 case WNNC_PRINTING:
316                 /* returns mask of the supported printing functions */
317                 return  WNNC_PRT_OpenJob|WNNC_PRT_CloseJob|WNNC_PRT_HoldJob
318                         |WNNC_PRT_ReleaseJob|WNNC_PRT_CancelJob
319                         |WNNC_PRT_SetJobCopies|WNNC_PRT_WatchQueue
320                         |WNNC_PRT_UnwatchQueue|WNNC_PRT_LockQueueData
321                         |WNNC_PRT_UnlockQueueData|WNNC_PRT_AbortJob
322                         |WNNC_PRT_WriteJob;
323
324                 case WNNC_DIALOG:
325                 /* returns mask of the supported dialog functions */
326                 return  WNNC_DLG_DeviceMode|WNNC_DLG_BrowseDialog
327                         |WNNC_DLG_ConnectDialog|WNNC_DLG_DisconnectDialog
328                         |WNNC_DLG_ViewQueueDialog|WNNC_DLG_PropertyDialog
329                         |WNNC_DLG_ConnectionDialog
330                         /*|WNNC_DLG_PrinterConnectDialog
331                         |WNNC_DLG_SharesDialog|WNNC_DLG_ShareAsDialog*/;
332
333                 case WNNC_ADMIN:
334                 /* returns mask of the supported administration functions */
335                 /* not sure if long file names is a good idea */
336                 return  WNNC_ADM_GetDirectoryType|WNNC_ADM_DirectoryNotify
337                         |WNNC_ADM_LongNames/*|WNNC_ADM_SetDefaultDrive*/;
338
339                 case WNNC_ERROR:
340                 /* returns mask of the supported error functions */
341                 return  WNNC_ERR_GetError|WNNC_ERR_GetErrorText;
342
343                 case WNNC_PRINTMGREXT:
344                 /* returns the Print Manager version in major and 
345                    minor format if Print Manager functions are available */
346                 return 0x30e; /* printman version of WfW 3.11 */
347
348                 case 0xffff:
349                 /* Win 3.11 returns HMODULE of network driver here
350                 FIXME: what should we return ?
351                 logonoff.exe needs it, msmail crashes with wrong value */
352                 return 0;
353
354         default:
355                 return 0;
356         }
357 }
358
359 /**************************************************************************
360  *              WNetDeviceMode       [USER.514]
361  */
362 int WINAPI WNetDeviceMode(HWND16 hWndOwner)
363 {
364         FIXME(wnet, "(%04x): stub\n",hWndOwner);
365         return WN_NO_NETWORK;
366 }
367
368 /**************************************************************************
369  *              WNetBrowseDialog       [USER.515]
370  */
371 int WINAPI WNetBrowseDialog(HWND16 hParent,WORD nType,LPSTR szPath)
372 {
373         FIXME(wnet, "(%04x,%x,'%s'): stub\n",hParent,nType,szPath);
374         return WN_NO_NETWORK;
375 }
376
377 /**************************************************************************
378  *                              WNetGetUser                     [USER.516]
379  */
380 UINT16 WINAPI WNetGetUser(LPSTR lpLocalName, LPSTR lpUserName, DWORD *lpSize)
381 {
382         FIXME(wnet, "(%p, %p, %p): stub\n", lpLocalName, lpUserName, lpSize);
383         return WN_NO_NETWORK;
384 }
385
386 /**************************************************************************
387  *              WNetGetError       [USER.519]
388  */
389 int WINAPI WNetGetError(LPWORD nError)
390 {
391         FIXME(wnet, "(%p): stub\n",nError);
392         return WN_NO_NETWORK;
393 }
394
395 /**************************************************************************
396  *              WNetGetErrorText       [USER.520]
397  */
398 int WINAPI WNetGetErrorText(WORD nError, LPSTR lpBuffer, LPWORD nBufferSize)
399 {
400         FIXME(wnet, "(%x,%p,%p): stub\n",nError,lpBuffer,nBufferSize);
401         return WN_NET_ERROR;
402 }
403
404 /**************************************************************************
405  *              WNetRestoreConnection       [USER.523]
406  */
407 int WINAPI WNetRestoreConnection(HWND16 hwndOwner,LPSTR lpszDevice)
408 {
409         FIXME(wnet, "(%04x,'%s'): stub\n",hwndOwner,lpszDevice);
410         return WN_NO_NETWORK;
411 }
412
413 /**************************************************************************
414  *              WNetWriteJob       [USER.524]
415  */
416 int WINAPI WNetWriteJob(HANDLE16 hJob,void *lpData,LPWORD lpcbData)
417 {
418         FIXME(wnet, "(%04x,%p,%p): stub\n",hJob,lpData,lpcbData);
419         return WN_NO_NETWORK;
420 }
421
422 /********************************************************************
423  *              WNetConnectDialog       [USER.525]
424  */
425 UINT16 WINAPI WNetConnectDialog(HWND16 hWndParent, WORD iType)
426 {
427         FIXME(wnet, "(%04x, %4X): stub\n", hWndParent, iType);
428         return WN_SUCCESS;
429 }
430
431 /**************************************************************************
432  *              WNetDisconnectDialog       [USER.526]
433  */
434 int WINAPI WNetDisconnectDialog(HWND16 hwndOwner, WORD iType)
435 {
436         FIXME(wnet, "(%04x,%x): stub\n",hwndOwner,iType);
437         return WN_NO_NETWORK;
438 }
439
440 /**************************************************************************
441  *              WnetConnectionDialog     [USER.527]
442  */
443 UINT16 WINAPI WNetConnectionDialog(HWND16 hWndParent, WORD iType)
444 {
445         FIXME(wnet, "(%04x, %4X): stub\n", hWndParent, iType);
446         return WN_SUCCESS;
447 }
448
449
450
451 /**************************************************************************
452  *              WNetViewQueueDialog       [USER.528]
453  */
454 int WINAPI WNetViewQueueDialog(HWND16 hwndOwner,LPSTR lpszQueue)
455 {
456         FIXME(wnet, "(%04x,'%s'): stub\n",hwndOwner,lpszQueue);
457         return WN_NO_NETWORK;
458 }
459
460 /**************************************************************************
461  *              WNetPropertyDialog       [USER.529]
462  */
463 int WINAPI WNetPropertyDialog(HWND16 hwndParent,WORD iButton,
464                               WORD nPropSel,LPSTR lpszName,WORD nType)
465 {
466         FIXME(wnet, "(%04x,%x,%x,'%s',%x): stub\n",
467               hwndParent,iButton,nPropSel,lpszName,nType);
468         return WN_NO_NETWORK;
469 }
470
471 /*********************************************************************
472  *  WNetGetDirectoryType [USER.530]  Decides whether resource is local
473  *
474  * RETURNS
475  *    on success,  puts one of the following in *lpType:
476  * - WNDT_NETWORK   on a network
477  * - WNDT_LOCAL     local
478  */
479 int WINAPI WNetGetDirectoryType16(LPSTR lpName, LPINT16 lpType)
480 {
481         UINT32 type = GetDriveType32A(lpName);
482
483         if (type == DRIVE_DOESNOTEXIST)
484           type = GetDriveType32A(NULL);
485         *lpType = (type==DRIVE_REMOTE)?WNDT_NETWORK:WNDT_NORMAL;
486         TRACE(wnet,"%s is %s\n",lpName,(*lpType==WNDT_NETWORK)?
487               "WNDT_NETWORK":"WNDT_NORMAL");
488         return WN_SUCCESS;
489 }
490
491 /*****************************************************************
492  *              WNetGetDirectoryTypeA     [MPR.109]
493  */
494
495 UINT32 WINAPI WNetGetDirectoryType32A(LPSTR lpName,void *lpType)
496 {
497    return WNetGetDirectoryType16(lpName, lpType);
498 }
499
500 /**************************************************************************
501  *              WNetDirectoryNotify       [USER.531]
502  */
503 int WINAPI WNetDirectoryNotify(HWND16 hwndOwner,void *lpDir,WORD wOper)
504 {
505         FIXME(wnet, "(%04x,%p,%x): stub\n",hwndOwner,lpDir,wOper);
506         return WN_NO_NETWORK;
507 }
508
509 /**************************************************************************
510  *              WNetGetPropertyText       [USER.532]
511  */
512 int WINAPI WNetGetPropertyText(HWND16 hwndParent,WORD iButton,WORD nPropSel,
513                                LPSTR lpszName,WORD nType)
514 {
515         FIXME(wnet, "(%04x,%x,%x,'%s',%x): stub\n",
516               hwndParent,iButton,nPropSel,lpszName,nType);
517         return WN_NO_NETWORK;
518 }
519
520
521 /**************************************************************************
522  *                              WNetCloseEnum           [USER.???]
523  */
524 UINT16 WINAPI WNetCloseEnum(HANDLE16 hEnum)
525 {
526         FIXME(wnet, "(%04x): stub\n", hEnum);
527         return WN_NO_NETWORK;
528 }
529
530 /**************************************************************************
531  *                              WNetEnumResource        [USER.???]
532  */
533 UINT16 WINAPI WNetEnumResource(HANDLE16 hEnum, DWORD cRequ, 
534                                DWORD *lpCount, LPVOID lpBuf)
535 {
536         FIXME(wnet, "(%04x, %08lX, %p, %p): stub\n", 
537               hEnum, cRequ, lpCount, lpBuf);
538         return WN_NO_NETWORK;
539 }
540
541 /**************************************************************************
542  *                              WNetOpenEnum            [USER.???]
543  */
544 UINT16 WINAPI WNetOpenEnum16(DWORD dwScope, DWORD dwType, 
545                              LPNETRESOURCE16 lpNet, HANDLE16 *lphEnum)
546 {
547         FIXME(wnet, "(%08lX, %08lX, %p, %p): stub\n",
548               dwScope, dwType, lpNet, lphEnum);
549         return WN_NO_NETWORK;
550 }
551
552 /**************************************************************************
553  *                              WNetOpenEnumA           [MPR.92]
554  */
555 UINT32 WINAPI WNetOpenEnum32A(DWORD dwScope, DWORD dwType, 
556                               LPNETRESOURCE32A lpNet, HANDLE32 *lphEnum)
557 {
558         FIXME(wnet, "(%08lX, %08lX, %p, %p): stub\n",
559               dwScope, dwType, lpNet, lphEnum);
560         return WN_NO_NETWORK;
561 }
562
563 /* ****************************************************************
564  *    WNetGetResourceInformationA [MPR.80]
565  * */
566
567 DWORD WINAPI 
568 WNetGetResourceInformation32A(
569         LPNETRESOURCE32A netres,LPVOID buf,LPDWORD buflen,LPSTR systemstr
570 ) {
571         FIXME(wnet,"(%p,%p,%p,%p): stub!\n",netres,buf,buflen,systemstr);
572         return WN_NO_NETWORK;
573 }
574
575 /**************************************************************************
576  * WNetCachePassword [MPR.52]  Saves password in cache
577  *
578  * RETURNS
579  *    Success: WN_SUCCESS
580  *    Failure: WNACCESS_DENIED, WN_BAD_PASSWORD, WN_BADVALUE, WN_NET_ERROR,
581  *             WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY
582  */
583 DWORD WINAPI WNetCachePassword(
584     LPSTR pbResource, /* [in] Name of workgroup, computer, or resource */
585     WORD cbResource,  /* [in] Size of name */
586     LPSTR pbPassword, /* [in] Buffer containing password */
587     WORD cbPassword,  /* [in] Size of password */
588     BYTE nType)       /* [in] Type of password to cache */
589 {
590     FIXME(mpr,"(%s,%d,%s,%d,%d): stub\n", pbResource,cbResource,
591           pbPassword,cbPassword,nType);
592     return WN_SUCCESS;
593 }
594
595
596
597 /*****************************************************************
598  * WNetGetCachedPassword [MPR.69]  Retrieves password from cache
599  *
600  * RETURNS
601  *    Success: WN_SUCCESS
602  *    Failure: WNACCESS_DENIED, WN_BAD_PASSWORD, WN_BAD_VALUE, 
603  *             WN_NET_ERROR, WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY
604  */
605 DWORD WINAPI WNetGetCachedPassword(
606     LPSTR pbResource,   /* [in]  Name of workgroup, computer, or resource */
607     WORD cbResource,    /* [in]  Size of name */
608     LPSTR pbPassword,   /* [out] Buffer to receive password */
609     LPWORD pcbPassword, /* [out] Receives size of password */
610     BYTE nType)         /* [in]  Type of password to retrieve */
611 {
612     FIXME(mpr,"(%s,%d,%p,%d,%d): stub\n",
613           pbResource,cbResource,pbPassword,*pcbPassword,nType);
614     return WN_ACCESS_DENIED;
615 }
616
617 /* ****************************************************************
618  *     MultinetGetConnectionPerformanceA [MPR.25]
619  *
620  * RETURNS
621  *    Success: NO_ERROR
622  *    Failure: ERROR_NOT_SUPPORTED, ERROR_NOT_CONNECTED,
623  *             ERROR_NO_NET_OR_BAD_PATH, ERROR_BAD_DEVICE,
624  *             ERROR_BAD_NET_NAME, ERROR_INVALID_PARAMETER, 
625  *             ERROR_NO_NETWORK, ERROR_EXTENDED_ERROR
626  */
627 DWORD WINAPI MultinetGetConnectionPerformance32A(
628         LPNETRESOURCE32A lpNetResource,
629         LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct
630 ) {
631         FIXME(mpr,"(%p,%p): stub\n",lpNetResource,lpNetConnectInfoStruct);
632         return 1;
633 }