Windows: AFSTearDownExtents may experience active extents
[openafs.git] / src / WINNT / client_config / tab_prefs.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <winsock2.h>
11 #include <ws2tcpip.h>
12
13 extern "C" {
14 #include <afs/param.h>
15 #include <afs/stds.h>
16 }
17
18 #include "afs_config.h"
19 #include "tab_prefs.h"
20 #include <hashlist.h>
21 #include <stdlib.h>
22
23
24 /*
25  * VARIABLES __________________________________________________________________
26  *
27  */
28
29 static struct l
30    {
31    CRITICAL_SECTION cs;
32    BOOL fThreadActive;
33    HWND hList;
34    } l;
35
36 #define cREALLOC_PREFS   32
37
38 #ifndef iswhite
39 #define iswhite(_ch) (((_ch)==TEXT(' ')) || ((_ch)==TEXT('\t')))
40 #endif
41 #ifndef iseol
42 #define iseol(_ch) (((_ch)==TEXT('\r')) || ((_ch)==TEXT('\n')))
43 #endif
44 #ifndef iswhiteeol
45 #define iswhiteeol(_ch) (iswhite(_ch) || iseol(_ch))
46 #endif
47
48
49 /*
50  * PROTOTYPES _________________________________________________________________
51  *
52  */
53
54 void PrefsTab_OnInitDialog (HWND hDlg);
55 BOOL PrefsTab_OnApply (HWND hDlg);
56 void PrefsTab_OnRefresh (HWND hDlg);
57 void PrefsTab_OnFillList (HWND hDlg);
58 void PrefsTab_OnSelect (HWND hDlg);
59 void PrefsTab_OnUpDown (HWND hDlg, BOOL fDown);
60 void PrefsTab_OnAdd (HWND hDlg);
61 void PrefsTab_OnEdit (HWND hDlg);
62 void PrefsTab_OnImport (HWND hDlg);
63
64 void PrefsTab_MergeServerPrefs (PSERVERPREFS pGlobal, PSERVERPREFS pAdd);
65 void PrefsTab_AddItem (HWND hDlg, PSERVERPREF pPref, BOOL fSelect);
66 void PrefsTab_AddItem (HWND hDlg, LPCTSTR pszServer, int iRank);
67
68 DWORD WINAPI PrefsTab_RefreshThread (PVOID lp);
69 DWORD WINAPI PrefsTab_ThreadProc (PVOID lp);
70 void PrefsTab_ThreadProcFunc (PSERVERPREFS pPrefs, BOOL *pfStopFlag);
71
72 int CALLBACK PrefsTab_SortFunction (HWND hList, HLISTITEM hItem1, LPARAM lpItem1, HLISTITEM hItem2, LPARAM lpItem2);
73
74 BOOL CALLBACK IPKey_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData);
75 HASHVALUE CALLBACK IPKey_HashObject (LPHASHLISTKEY pKey, PVOID pObject);
76 HASHVALUE CALLBACK IPKey_HashData (LPHASHLISTKEY pKey, PVOID pData);
77
78 BOOL CALLBACK PrefsEdit_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
79 void PrefsEdit_OnInitDialog (HWND hDlg);
80 void PrefsEdit_OnOK (HWND hDlg);
81 void PrefsEdit_Enable (HWND hDlg);
82
83
84 /*
85  * ROUTINES ___________________________________________________________________
86  *
87  */
88
89 BOOL CALLBACK PrefsTab_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
90 {
91    switch (msg)
92       {
93       case WM_INITDIALOG:
94          InitializeCriticalSection (&l.cs);
95          PrefsTab_OnInitDialog (hDlg);
96          break;
97
98       case WM_COMMAND:
99          switch (LOWORD(wp))
100             {
101             case IDAPPLY:
102                if (!PrefsTab_OnApply (hDlg))
103                   SetWindowLongPtr (hDlg, DWLP_MSGRESULT, TRUE);
104                break;
105
106             case IDC_REFRESH:
107                PrefsTab_OnRefresh (hDlg);
108                break;
109
110             case IDC_SHOW_FS:
111             case IDC_SHOW_VLS:
112                PrefsTab_OnFillList (hDlg);
113                break;
114
115             case IDC_ADD:
116                PrefsTab_OnAdd (hDlg);
117                break;
118
119             case IDC_EDIT:
120                PrefsTab_OnEdit (hDlg);
121                break;
122
123             case IDC_UP:
124                PrefsTab_OnUpDown (hDlg, FALSE);
125                break;
126
127             case IDC_DOWN:
128                PrefsTab_OnUpDown (hDlg, TRUE);
129                break;
130
131             case IDC_IMPORT:
132                PrefsTab_OnImport (hDlg);
133                break;
134
135             case IDHELP:
136                PrefsTab_DlgProc (hDlg, WM_HELP, 0, 0);
137                break;
138             }
139          break;
140
141       case WM_HELP:
142          WinHelp (hDlg, g.szHelpFile, HELP_CONTEXT, IDH_AFSCONFIG_PREFS_NT);
143          break;
144
145       case WM_NOTIFY:
146          switch (((LPNMHDR)lp)->code)
147             {
148             case FLN_ITEMSELECT:
149                PrefsTab_OnSelect (hDlg);
150                break;
151
152             case FLN_LDBLCLICK:
153                if (IsWindowEnabled (GetDlgItem (hDlg, IDC_EDIT)))
154                   PrefsTab_OnEdit (hDlg);
155                break;
156             }
157          break;
158       }
159
160    return FALSE;
161 }
162
163
164 void PrefsTab_OnInitDialog (HWND hDlg)
165 {
166    HICON hiUp = TaLocale_LoadIcon (IDI_UP);
167    HICON hiDown = TaLocale_LoadIcon (IDI_DOWN);
168
169    SendDlgItemMessage (hDlg, IDC_UP, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hiUp);
170    SendDlgItemMessage (hDlg, IDC_DOWN, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hiDown);
171
172    CheckDlgButton (hDlg, IDC_SHOW_FS, TRUE);
173    CheckDlgButton (hDlg, IDC_SHOW_VLS, FALSE);
174
175    l.hList = GetDlgItem (hDlg, IDC_LIST);
176
177    FASTLISTCOLUMN Column;
178    Column.dwFlags = FLCF_JUSTIFY_LEFT;
179    Column.cxWidth = 200;
180    GetString (Column.szText, IDS_PREFCOL_SERVER);
181    FastList_SetColumn (l.hList, 0, &Column);
182
183    Column.dwFlags = FLCF_JUSTIFY_RIGHT;
184    Column.cxWidth = 40;
185    GetString (Column.szText, IDS_PREFCOL_RANK);
186    FastList_SetColumn (l.hList, 1, &Column);
187
188    FastList_SetSortFunction (l.hList, PrefsTab_SortFunction);
189
190    PrefsTab_OnFillList (hDlg);
191    PrefsTab_OnRefresh (hDlg);
192 }
193
194
195 BOOL PrefsTab_CommitChanges (BOOL fForce)
196 {
197    HWND hDlg;
198    if ((hDlg = PropSheet_FindTabWindow (g.psh, (DLGPROC)PrefsTab_DlgProc)) == NULL)
199       return TRUE;
200    if (fForce)
201       SetWindowLongPtr (hDlg, DWLP_MSGRESULT, FALSE); // Make sure we try to apply
202    if (PrefsTab_OnApply (hDlg))
203       return TRUE;
204    SetWindowLongPtr (hDlg, DWLP_MSGRESULT, TRUE);
205    return FALSE;
206 }
207
208
209 BOOL PrefsTab_OnApply (HWND hDlg)
210 {
211    // Don't try to do anything if we've already failed the apply
212    if (GetWindowLongPtr (hDlg, DWLP_MSGRESULT))
213       return FALSE;
214
215    if (g.Configuration.pFServers && g.Configuration.fChangedPrefs)
216       {
217       if (!Config_SetServerPrefs (g.Configuration.pFServers))
218          return FALSE;
219       }
220    if (g.Configuration.pVLServers && g.Configuration.fChangedPrefs)
221       {
222       if (!Config_SetServerPrefs (g.Configuration.pVLServers))
223          return FALSE;
224       }
225    g.Configuration.fChangedPrefs = FALSE;
226    return TRUE;
227 }
228
229
230 void PrefsTab_OnRefresh (HWND hDlg)
231 {
232    DWORD idThread;
233    CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)PrefsTab_RefreshThread, (PVOID)hDlg, 0, &idThread);
234 }
235
236
237 void PrefsTab_OnFillList (HWND hDlg)
238 {
239    EnterCriticalSection (&l.cs);
240    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
241
242    // Empty the fastlist, and clear from the lists any mention of HLISTITEMs.
243    //
244    FastList_Begin (l.hList);
245    FastList_RemoveAll (l.hList);
246
247    if (g.Configuration.pVLServers)
248       {
249       for (size_t ii = 0; ii < g.Configuration.pVLServers->cPrefs; ++ii)
250          g.Configuration.pVLServers->aPrefs[ ii ].hItem = NULL;
251       }
252    if (g.Configuration.pFServers)
253       {
254       for (size_t ii = 0; ii < g.Configuration.pFServers->cPrefs; ++ii)
255          g.Configuration.pFServers->aPrefs[ ii ].hItem = NULL;
256       }
257
258    // Fill in the fastlist by adding entries from the appropriate prefslist.
259    //
260    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
261    if (pPrefs)
262       {
263       for (size_t ii = 0; ii < pPrefs->cPrefs; ++ii)
264          {
265          if (!pPrefs->aPrefs[ ii ].ipServer)
266             continue;
267
268          TCHAR szItem[ cchRESOURCE ];
269          if (!pPrefs->aPrefs[ ii ].szServer[0])
270             {
271             lstrcpy (szItem, inet_ntoa (*(struct in_addr *)&pPrefs->aPrefs[ ii ].ipServer));
272             }
273          else
274             {
275             wsprintf (szItem, TEXT("%s (%s)"),
276                       pPrefs->aPrefs[ ii ].szServer,
277                       inet_ntoa (*(struct in_addr *)&pPrefs->aPrefs[ ii ].ipServer));
278             }
279
280          FASTLISTADDITEM ai;
281          memset (&ai, 0x00, sizeof(FASTLISTADDITEM));
282          ai.iFirstImage = IMAGE_NOIMAGE;
283          ai.iSecondImage = IMAGE_NOIMAGE;
284          ai.pszText = szItem;
285          ai.lParam = ii;
286          pPrefs->aPrefs[ ii ].hItem = FastList_AddItem (l.hList, &ai);
287
288          wsprintf (szItem, TEXT("%ld"), pPrefs->aPrefs[ ii ].iRank);
289          FastList_SetItemText (l.hList, pPrefs->aPrefs[ ii ].hItem, 1, szItem);
290          }
291       }
292
293    // Okay, we're done!
294    //
295    FastList_End (l.hList);
296    LeaveCriticalSection (&l.cs);
297    PrefsTab_OnSelect (hDlg);
298 }
299
300
301 void PrefsTab_OnSelect (HWND hDlg)
302 {
303    if (IsWindowEnabled (l.hList))
304       {
305       HLISTITEM hItem = FastList_FindFirstSelected (l.hList);
306       HLISTITEM hItemFirst = FastList_FindFirst (l.hList);
307       HLISTITEM hItemNext = FastList_FindNext (l.hList, hItem);
308
309       EnableWindow (GetDlgItem (hDlg, IDC_UP), (hItem && (hItem != hItemFirst)));
310       EnableWindow (GetDlgItem (hDlg, IDC_DOWN), (hItem && hItemNext));
311       EnableWindow (GetDlgItem (hDlg, IDC_ADD), TRUE);
312       EnableWindow (GetDlgItem (hDlg, IDC_IMPORT), TRUE);
313       EnableWindow (GetDlgItem (hDlg, IDC_EDIT), !!hItem);
314       }
315 }
316
317
318 void PrefsTab_OnUpDown (HWND hDlg, BOOL fDown)
319 {
320    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
321    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
322
323    HLISTITEM hItem;
324    if ((hItem = FastList_FindFirstSelected (l.hList)) == NULL)
325       return;
326
327    HLISTITEM hOther;
328    hOther = (fDown) ? FastList_FindNext(l.hList,hItem) : FastList_FindPrevious(l.hList,hItem);
329    if (hOther == NULL)
330       return;
331
332    size_t iItem = (size_t)FastList_GetItemParam (l.hList, hItem);
333    size_t iOther = (size_t)FastList_GetItemParam (l.hList, hOther);
334
335    if (!pPrefs || (pPrefs->cPrefs <= iItem) || (pPrefs->cPrefs <= iOther))
336       return;
337
338    FastList_Begin (l.hList);
339
340    PSERVERPREF pPref1 = &pPrefs->aPrefs[ iItem ];
341    PSERVERPREF pPref2 = &pPrefs->aPrefs[ iOther ];
342
343    if (pPref1->iRank == pPref2->iRank)
344       {
345       if (fDown && (pPref1->iRank < 65534))
346          pPref1->iRank ++;
347       else if ((!fDown) && (pPref1->iRank > 1))
348          pPref1->iRank --;
349       pPref1->fChanged = TRUE;
350       }
351    else // (pPref1->iRating != pPref2->iRating)
352       {
353       pPref1->iRank ^= pPref2->iRank;
354       pPref2->iRank ^= pPref1->iRank;
355       pPref1->iRank ^= pPref2->iRank;
356       pPref1->fChanged = TRUE;
357       pPref2->fChanged = TRUE;
358       }
359
360    TCHAR szText[ cchRESOURCE ];
361    wsprintf (szText, TEXT("%ld"), pPref1->iRank);
362    FastList_SetItemText (l.hList, pPref1->hItem, 1, szText);
363
364    wsprintf (szText, TEXT("%ld"), pPref2->iRank);
365    FastList_SetItemText (l.hList, pPref2->hItem, 1, szText);
366
367    FastList_EnsureVisible (l.hList, hItem);
368    FastList_End (l.hList);
369    PrefsTab_OnSelect (hDlg);
370
371    g.Configuration.fChangedPrefs = TRUE;
372 }
373
374
375 void PrefsTab_OnAdd (HWND hDlg)
376 {
377    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
378    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
379
380    SERVERPREF Pref;
381    memset (&Pref, 0x00, sizeof(SERVERPREF));
382    Pref.iRank = 30000;
383
384    if (ModalDialogParam (IDD_PREFS_EDIT, GetParent(hDlg), (DLGPROC)PrefsEdit_DlgProc, (LPARAM)&Pref) == IDOK)
385       {
386       PrefsTab_AddItem (hDlg, &Pref, TRUE);
387       PrefsTab_OnSelect (hDlg);
388       g.Configuration.fChangedPrefs = TRUE;
389       }
390 }
391
392
393 void PrefsTab_OnEdit (HWND hDlg)
394 {
395    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
396    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
397
398    HLISTITEM hItem;
399    if ((hItem = FastList_FindFirstSelected (l.hList)) == NULL)
400       return;
401
402    PSERVERPREF pPref = &pPrefs->aPrefs[ FastList_GetItemParam (l.hList, hItem) ];
403
404    if (ModalDialogParam (IDD_PREFS_EDIT, GetParent(hDlg), (DLGPROC)PrefsEdit_DlgProc, (LPARAM)pPref) == IDOK)
405       {
406       FastList_Begin (l.hList);
407
408       TCHAR szText[ cchRESOURCE ];
409       wsprintf (szText, TEXT("%ld"), pPref->iRank);
410       FastList_SetItemText (l.hList, pPref->hItem, 1, szText);
411       pPref->fChanged = TRUE;
412
413       FastList_EnsureVisible (l.hList, hItem);
414       FastList_End (l.hList);
415       PrefsTab_OnSelect (hDlg);
416       g.Configuration.fChangedPrefs = TRUE;
417       }
418 }
419
420
421 void PrefsTab_OnImport (HWND hDlg)
422 {
423    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
424    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
425
426    TCHAR szFilename[ MAX_PATH ] = TEXT("");
427    if (Browse_Open (hDlg, szFilename, NULL, IDS_FILTER_TXT, 0, NULL, 0))
428       {
429       FastList_Begin (l.hList);
430
431       // Open the file and read it into memory.
432       //
433       HANDLE fh;
434       if ((fh = CreateFile (szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
435          {
436          DWORD cbLength = GetFileSize (fh, NULL);
437          LPTSTR pszBuffer = (LPTSTR)Allocate (sizeof(TCHAR) * (cbLength +2));
438
439          DWORD cbRead;
440          if (ReadFile (fh, pszBuffer, cbLength, &cbRead, NULL))
441             {
442             pszBuffer[ cbRead ] = TEXT('\0');
443             pszBuffer[ cbRead+1 ] = TEXT('\0');
444
445             // Scan the file line-by-line...
446             //
447             LPTSTR pszStart = pszBuffer;
448             while (pszStart && *pszStart)
449                {
450                while (iswhiteeol(*pszStart))
451                   ++pszStart;
452
453                LPTSTR pszEnd = pszStart;
454                while (*pszEnd && !iseol(*pszEnd))
455                   ++pszEnd;
456                *pszEnd++ = TEXT('\0');
457
458                // Okay, {pszStart} points to a 0-terminated line in this file.
459                // If the line starts with '#', ';' or '//', skip it.
460                //
461                if ( (pszStart[0] != TEXT('#')) &&
462                     (pszStart[0] != TEXT(';')) &&
463                    ((pszStart[0] != TEXT('/')) || (pszStart[1] != TEXT('/'))) )
464                   {
465                   // Break the line up into two sections: the machine name,
466                   // and the ranking.
467                   //
468                   TCHAR szServer[ MAX_PATH ];
469                   LPTSTR pszOut;
470                   for (pszOut = szServer; *pszStart && !iswhite(*pszStart); )
471                      *pszOut++ = *pszStart++;
472                   *pszOut = TEXT('\0');
473
474                   while (iswhite(*pszStart))
475                      ++pszStart;
476
477                   TCHAR szRank[ MAX_PATH ];
478                   for (pszOut = szRank; *pszStart && !iswhite(*pszStart); )
479                      *pszOut++ = *pszStart++;
480                   *pszOut = TEXT('\0');
481
482                   PrefsTab_AddItem (hDlg, szServer, atoi(szRank));
483                   }
484
485                // Process the next line in the file.
486                //
487                pszStart = pszEnd;
488                }
489             }
490
491          Free (pszBuffer);
492          CloseHandle (fh);
493          }
494
495       // Restart the background thread, to resolve unknown IP address
496       //
497       PrefsTab_OnRefresh (hDlg);
498       FastList_End (l.hList);
499       g.Configuration.fChangedPrefs = TRUE;
500       }
501 }
502
503
504 void PrefsTab_MergeServerPrefs (PSERVERPREFS pGlobal, PSERVERPREFS pAdd)
505 {
506    LPHASHLIST pList = New (HASHLIST);
507    LPHASHLISTKEY pKey = pList->CreateKey (TEXT("IP Address"), IPKey_Compare, IPKey_HashObject, IPKey_HashData);
508
509    size_t ii;
510    for (ii = 0; ii < pGlobal->cPrefs; ++ii)
511       {
512       if (!pGlobal->aPrefs[ ii ].ipServer)
513          continue;
514       pList->Add (&pGlobal->aPrefs[ ii ]);
515       }
516
517    size_t iOut = 0;
518    for (ii = 0; ii < pAdd->cPrefs; ++ii)
519       {
520       if (!pAdd->aPrefs[ ii ].ipServer)
521          continue;
522
523       // The whole point of using a hashlist here is to allow this next call--
524       // on a hashlist, lookup and add are both constant-time, turning this
525       // merge into O(N) instead of (O(N^2))
526       //
527       if (pKey->GetFirstObject (&pAdd->aPrefs[ ii ].ipServer))
528          continue;
529
530       for ( ; iOut < pGlobal->cPrefs; ++iOut)
531          {
532          if (!pGlobal->aPrefs[ iOut ].ipServer)
533             break;
534          }
535
536       if (REALLOC (pGlobal->aPrefs, pGlobal->cPrefs, 1+iOut, cREALLOC_PREFS))
537          {
538          memcpy (&pGlobal->aPrefs[ iOut ], &pAdd->aPrefs[ ii ], sizeof(SERVERPREFS));
539          iOut++;
540          }
541       }
542
543    Delete (pList);
544 }
545
546
547 void PrefsTab_AddItem (HWND hDlg, PSERVERPREF pPref, BOOL fSelect)
548 {
549    BOOL fVLServers = IsDlgButtonChecked (hDlg, IDC_SHOW_VLS);
550    PSERVERPREFS pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
551
552    size_t ii;
553    for (ii = 0; ii < pPrefs->cPrefs; ++ii)
554       {
555       if (pPrefs->aPrefs[ ii ].ipServer == pPref->ipServer)
556          break;
557       }
558    if (ii == pPrefs->cPrefs)
559       {
560       for (ii = 0; ii < pPrefs->cPrefs; ++ii)
561          {
562          if (!pPrefs->aPrefs[ ii ].ipServer)
563             break;
564          }
565       if (!REALLOC (pPrefs->aPrefs, pPrefs->cPrefs, 1+ii, cREALLOC_PREFS))
566          return;
567       memcpy (&pPrefs->aPrefs[ ii ], pPref, sizeof(SERVERPREF));
568       }
569
570    FastList_Begin (l.hList);
571
572    if (!pPrefs->aPrefs[ ii ].hItem)
573       {
574       TCHAR szItem[ cchRESOURCE ];
575       if (!pPrefs->aPrefs[ ii ].szServer[0])
576          {
577          lstrcpy (szItem, inet_ntoa (*(struct in_addr *)&pPrefs->aPrefs[ ii ].ipServer));
578          }
579       else
580          {
581          wsprintf (szItem, TEXT("%s (%s)"),
582                    pPrefs->aPrefs[ ii ].szServer,
583                    inet_ntoa (*(struct in_addr *)&pPrefs->aPrefs[ ii ].ipServer));
584          }
585
586       FASTLISTADDITEM ai;
587       memset (&ai, 0x00, sizeof(FASTLISTADDITEM));
588       ai.iFirstImage = IMAGE_NOIMAGE;
589       ai.iSecondImage = IMAGE_NOIMAGE;
590       ai.pszText = szItem;
591       ai.lParam = ii;
592       pPrefs->aPrefs[ ii ].hItem = FastList_AddItem (l.hList, &ai);
593       }
594
595    TCHAR szText[ cchRESOURCE ];
596    wsprintf (szText, TEXT("%ld"), pPrefs->aPrefs[ ii ].iRank);
597    FastList_SetItemText (l.hList, pPrefs->aPrefs[ ii ].hItem, 1, szText);
598    pPrefs->aPrefs[ ii ].fChanged = TRUE;
599
600    FastList_End (l.hList);
601
602    if (fSelect)
603       {
604       FastList_SelectItem (l.hList, pPrefs->aPrefs[ ii ].hItem, TRUE);
605       FastList_SetFocus (l.hList, pPrefs->aPrefs[ ii ].hItem);
606       FastList_EnsureVisible (l.hList, pPrefs->aPrefs[ ii ].hItem);
607       PrefsTab_OnSelect (hDlg);
608       }
609 }
610
611
612 void PrefsTab_AddItem (HWND hDlg, LPCTSTR pszServer, int iRank)
613 {
614    if ((iRank < 1) || (iRank > 65534))
615       return;
616
617    SERVERPREF Pref;
618    memset (&Pref, 0x00, sizeof(SERVERPREF));
619    Pref.iRank = iRank;
620
621    // If the server's name is an IP address, we'll translate it later
622    // when we do them en masse.
623    //
624    if (isdigit (pszServer[0]))
625       {
626       if ((Pref.ipServer = inet_addr (pszServer)) == INADDR_NONE)
627          return;
628       }
629    else // (!isdigit (pszServer[0]))
630       {
631       HOSTENT *pEntry;
632       if ((pEntry = gethostbyname (pszServer)) != NULL)
633          {
634          lstrcpy (Pref.szServer, pEntry->h_name);
635          Pref.ipServer = *(int *)pEntry->h_addr;
636          }
637       }
638
639    PrefsTab_AddItem (hDlg, &Pref, FALSE);
640 }
641
642
643 DWORD WINAPI PrefsTab_RefreshThread (PVOID lp)
644 {
645    HWND hDlg = (HWND)lp;
646    static BOOL *pfStopFlag = NULL;
647
648    // We may have a background thread or two working on resolving IP addresses.
649    // Flag them to stop.
650    //
651    EnterCriticalSection (&l.cs);
652    if (l.fThreadActive)
653       {
654       if (pfStopFlag)
655          *pfStopFlag = FALSE;
656       }
657    pfStopFlag = NULL; // Thread will free this when it terminates
658
659    // Retrieve PSERVERPREFS structures, and merge them into our globals
660    //
661    PSERVERPREFS pVLServers = Config_GetServerPrefs (TRUE);
662    PSERVERPREFS pFServers = Config_GetServerPrefs (FALSE);
663
664    if (!g.Configuration.pVLServers)
665       g.Configuration.pVLServers = pVLServers;
666    else if (g.Configuration.pVLServers && pVLServers)
667       PrefsTab_MergeServerPrefs (g.Configuration.pVLServers, pVLServers);
668
669    if (!g.Configuration.pFServers)
670       g.Configuration.pFServers = pFServers;
671    else if (g.Configuration.pFServers && pFServers)
672       PrefsTab_MergeServerPrefs (g.Configuration.pFServers, pFServers);
673
674    // Add entries to the fastlist
675    //
676    PrefsTab_OnFillList (hDlg);
677
678    // Fire up a background thread to resolve IP addresses into server names
679    //
680    pfStopFlag = New (BOOL);
681    *pfStopFlag = FALSE;
682
683    DWORD idThread;
684    CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)PrefsTab_ThreadProc, (PVOID)pfStopFlag, 0, &idThread);
685    l.fThreadActive = TRUE;
686
687    // Enable or disable controls based on whether the service is running
688    //
689    BOOL fRunning = (Config_GetServiceState() == SERVICE_RUNNING) ? TRUE : FALSE;
690
691    EnableWindow (GetDlgItem (hDlg, IDC_SHOW_FS), fRunning);
692    EnableWindow (GetDlgItem (hDlg, IDC_SHOW_VLS), fRunning);
693    EnableWindow (GetDlgItem (hDlg, IDC_LIST), fRunning);
694    EnableWindow (GetDlgItem (hDlg, IDC_UP), fRunning);
695    EnableWindow (GetDlgItem (hDlg, IDC_DOWN), fRunning);
696    EnableWindow (GetDlgItem (hDlg, IDC_IMPORT), fRunning);
697    EnableWindow (GetDlgItem (hDlg, IDC_ADD), fRunning);
698    EnableWindow (GetDlgItem (hDlg, IDC_EDIT), fRunning);
699    PrefsTab_OnSelect (hDlg);
700
701    TCHAR szText[ cchRESOURCE ];
702    GetString (szText, (fRunning) ? IDS_TIP_PREFS : IDS_WARN_STOPPED);
703    SetDlgItemText (hDlg, IDC_WARN, szText);
704
705    // We're done!
706    //
707    LeaveCriticalSection (&l.cs);
708    return 0;
709 }
710
711
712 DWORD WINAPI PrefsTab_ThreadProc (PVOID lp)
713 {
714    BOOL *pfStopFlag = (BOOL*)lp;
715    if (pfStopFlag)
716       {
717       PrefsTab_ThreadProcFunc (g.Configuration.pFServers, pfStopFlag);
718       PrefsTab_ThreadProcFunc (g.Configuration.pVLServers, pfStopFlag);
719
720       l.fThreadActive = FALSE;
721       Delete (pfStopFlag);
722       }
723    return 0;
724 }
725
726
727 void PrefsTab_ThreadProcFunc (PSERVERPREFS pPrefs, BOOL *pfStopFlag)
728 {
729    for (size_t ii = 0; ; ++ii)
730       {
731       // Find the next IP address to translate
732       //
733       EnterCriticalSection (&l.cs);
734
735       if ( (*pfStopFlag) || (ii >= pPrefs->cPrefs) )
736          {
737          LeaveCriticalSection (&l.cs);
738          break;
739          }
740
741       int ipServer;
742       if ( ((ipServer = pPrefs->aPrefs[ ii ].ipServer) == 0) ||
743            (pPrefs->aPrefs[ ii ].szServer[0] != TEXT('\0')) )
744          {
745          LeaveCriticalSection (&l.cs);
746          continue;
747          }
748
749       LeaveCriticalSection (&l.cs);
750
751       // Translate this IP address into a name
752       //
753       HOSTENT *pEntry;
754       if ((pEntry = gethostbyaddr ((char*)&ipServer, sizeof(ipServer), AF_INET)) == NULL)
755          continue;
756
757       // Update the SERVERPREFS list, and if necessary, update the display
758       // to show the server's name
759       //
760       EnterCriticalSection (&l.cs);
761
762       if (!*pfStopFlag)
763          {
764          if ((ii < pPrefs->cPrefs) && (ipServer == pPrefs->aPrefs[ ii ].ipServer))
765             {
766             lstrcpy (pPrefs->aPrefs[ ii ].szServer, pEntry->h_name);
767             if (pPrefs->aPrefs[ ii ].szServer[0])
768                {
769                if (pPrefs->aPrefs[ ii ].hItem)
770                   {
771                   TCHAR szItem[ cchRESOURCE ];
772                   wsprintf (szItem, TEXT("%s (%s)"),
773                             pPrefs->aPrefs[ ii ].szServer,
774                             inet_ntoa (*(struct in_addr *)&pPrefs->aPrefs[ ii ].ipServer));
775
776                   FastList_SetItemText (l.hList, pPrefs->aPrefs[ ii ].hItem, 0, szItem);
777                   }
778                }
779             }
780          }
781
782       LeaveCriticalSection (&l.cs);
783       }
784 }
785
786
787 int CALLBACK PrefsTab_SortFunction (HWND hList, HLISTITEM hItem1, LPARAM lpItem1, HLISTITEM hItem2, LPARAM lpItem2)
788 {
789    static PSERVERPREFS pPrefs = NULL;
790    if (!hItem1 || !hItem2)
791       {
792       BOOL fVLServers = IsDlgButtonChecked (GetParent(hList), IDC_SHOW_VLS);
793       pPrefs = (fVLServers) ? g.Configuration.pVLServers : g.Configuration.pFServers;
794       return 0;
795       }
796
797    if (!pPrefs || (pPrefs->cPrefs <= (size_t)lpItem1) || (pPrefs->cPrefs <= (size_t)lpItem2))
798       return 0;
799
800    PSERVERPREF pPref1 = &pPrefs->aPrefs[ lpItem1 ];
801    PSERVERPREF pPref2 = &pPrefs->aPrefs[ lpItem2 ];
802
803    if (pPref1->iRank != pPref2->iRank)
804       return pPref1->iRank - pPref2->iRank;
805
806    ULONG ip1 = (ULONG)htonl (pPref1->ipServer);
807    ULONG ip2 = (ULONG)htonl (pPref2->ipServer);
808    return (ip1 < ip2) ? -1 : 1;
809 }
810
811
812 BOOL CALLBACK IPKey_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
813 {
814    return (((PSERVERPREF)pObject)->ipServer == *(int*)pData);
815 }
816
817 HASHVALUE CALLBACK IPKey_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
818 {
819    return IPKey_HashData (pKey, &((PSERVERPREF)pObject)->ipServer);
820 }
821
822 HASHVALUE CALLBACK IPKey_HashData (LPHASHLISTKEY pKey, PVOID pData)
823 {
824    return *(int*)pData;
825 }
826
827
828 BOOL CALLBACK PrefsEdit_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
829 {
830    switch (msg)
831       {
832       case WM_INITDIALOG:
833          SetWindowLongPtr (hDlg, DWLP_USER, lp);
834          PrefsEdit_OnInitDialog (hDlg);
835          PrefsEdit_Enable (hDlg);
836          break;
837
838       case WM_COMMAND:
839          switch (LOWORD(wp))
840             {
841             case IDC_SERVER:
842                PrefsEdit_Enable (hDlg);
843                break;
844
845             case IDOK:
846                PrefsEdit_OnOK (hDlg);
847                break;
848
849             case IDCANCEL:
850                EndDialog (hDlg, IDCANCEL);
851                break;
852
853             case IDHELP:
854                PrefsEdit_DlgProc (hDlg, WM_HELP, 0, 0);
855                break;
856             }
857          break;
858
859       case WM_HELP:
860          WinHelp (hDlg, g.szHelpFile, HELP_CONTEXT, IDH_AFSCONFIG_PREFS_NT_ADDEDIT);
861          break;
862       }
863
864    return FALSE;
865 }
866
867
868 void PrefsEdit_OnInitDialog (HWND hDlg)
869 {
870    PSERVERPREF pPref = (PSERVERPREF)GetWindowLongPtr (hDlg, DWLP_USER);
871
872    if (pPref->ipServer)
873       EnableWindow (GetDlgItem (hDlg, IDC_SERVER), FALSE);
874
875    if (pPref->szServer[0])
876       {
877       SetDlgItemText (hDlg, IDC_SERVER, pPref->szServer);
878       }
879    else if (pPref->ipServer)
880       {
881       SetDlgItemText (hDlg, IDC_SERVER, inet_ntoa (*(struct in_addr *)&pPref->ipServer));
882       }
883
884    CreateSpinner (GetDlgItem (hDlg, IDC_RANK), 10, FALSE, 1, pPref->iRank, 65534);
885 }
886
887
888 void PrefsEdit_OnOK (HWND hDlg)
889 {
890    PSERVERPREF pPref = (PSERVERPREF)GetWindowLongPtr (hDlg, DWLP_USER);
891    pPref->iRank = SP_GetPos (GetDlgItem (hDlg, IDC_RANK));
892
893    if (IsWindowEnabled (GetDlgItem (hDlg, IDC_SERVER)))
894       {
895       BOOL rc = TRUE;
896       ULONG status = 0;
897
898       TCHAR szServer[ cchRESOURCE ];
899       GetDlgItemText (hDlg, IDC_SERVER, szServer, cchRESOURCE);
900       if (isdigit (szServer[0]))
901          {
902          if ((pPref->ipServer = inet_addr (szServer)) == INADDR_NONE)
903             {
904             rc = FALSE;
905             status = WSAGetLastError();
906             }
907          else
908             {
909             HOSTENT *pEntry;
910             if ((pEntry = gethostbyaddr ((char*)&pPref->ipServer, sizeof(pPref->ipServer), AF_INET)) != NULL)
911                lstrcpy (pPref->szServer, pEntry->h_name);
912             }
913          }
914       else // (!isdigit(pData->szServer[0]))
915          {
916          HOSTENT *pEntry;
917          if ((pEntry = gethostbyname (szServer)) == NULL)
918             {
919             rc = FALSE;
920             status = WSAGetLastError();
921             }
922          else
923             {
924             lstrcpy (pPref->szServer, pEntry->h_name);
925             pPref->ipServer = *(int *)pEntry->h_addr;
926             }
927          }
928
929       if (!rc)
930          {
931          Message (MB_ICONHAND | MB_OK, GetErrorTitle(), IDS_PREFERROR_RESOLVE, TEXT("%s%08lX"), szServer, status);
932          return;
933          }
934       }
935
936    EndDialog (hDlg, IDOK);
937 }
938
939
940 void PrefsEdit_Enable (HWND hDlg)
941 {
942    TCHAR szServer[ cchRESOURCE ];
943    GetDlgItemText (hDlg, IDC_SERVER, szServer, cchRESOURCE);
944
945    EnableWindow (GetDlgItem (hDlg, IDOK), !!szServer[0]);
946 }
947