]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WEClientCapabilities.m
added Z1 connector as a known user agent
[sope] / sope-appserver / NGObjWeb / WEClientCapabilities.m
1 /*
2   Copyright (C) 2000-2008 SKYRIX Software AG
3   Copyright (C) 2006-2008 Helge Hess
4
5   This file is part of SOPE.
6
7   SOPE is free software; you can redistribute it and/or modify it under
8   the terms of the GNU Lesser General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10   later version.
11
12   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15   License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with SOPE; see the file COPYING.  If not, write to the
19   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.
21 */
22
23 #include <NGObjWeb/WEClientCapabilities.h>
24 #include <NGObjWeb/WOAssociation.h>
25 #include <NGObjWeb/WOContext.h>
26 #include <NGObjWeb/WOComponent.h>
27 #include <NGObjWeb/WOResponse.h>
28 #include <string.h>
29 #include "common.h"
30
31 #define WEUA_UNKNOWN            0
32 #define WEUA_IE                 1
33 #define WEUA_Netscape           2
34 #define WEUA_Lynx               3
35 #define WEUA_Opera              4
36 #define WEUA_Amaya              5
37 #define WEUA_Emacs              6
38 #define WEUA_Wget               7
39 #define WEUA_WebFolder          8
40 #define WEUA_Mozilla            9
41 #define WEUA_OmniWeb           10
42 #define WEUA_iCab              11
43 #define WEUA_Konqueror         12
44 #define WEUA_Links             13
45 #define WEUA_DAVFS             14
46 #define WEUA_CADAVER           15
47 #define WEUA_GOLIVE            16
48 #define WEUA_MACOSX_DAVFS      17
49 #define WEUA_Dillo             18
50 #define WEUA_JavaSDK           19
51 #define WEUA_PythonURLLIB      20
52 #define WEUA_AppleDAVAccess    21
53 #define WEUA_MSWebPublisher    22
54 #define WEUA_CURL              23
55 #define WEUA_Evolution         24
56 #define WEUA_MSOutlook         25
57 #define WEUA_MSOutlookExpress  26
58 #define WEUA_GNOMEVFS          27
59 #define WEUA_ZideLook          28
60 #define WEUA_Safari            29
61 #define WEUA_SOUP              30
62 #define WEUA_Entourage         31
63 #define WEUA_NetNewsWire       32
64 #define WEUA_xmlrpclib_py      33
65 #define WEUA_Morgul            34
66 #define WEUA_CFNetwork         35
67 #define WEUA_KungLog           36
68 #define WEUA_SOPE              37
69 #define WEUA_Ecto              38
70 #define WEUA_NewsFire          39
71 #define WEUA_Goliath           40
72 #define WEUA_PerlHTTPDAV       41
73 #define WEUA_Google            42
74 #define WEUA_WebDrive          43
75 #define WEUA_Sunbird           44
76 #define WEUA_PEAR_XMLRPC       45
77 #define WEUA_Cook_XMLRPCdotNET 46
78 #define WEUA_WDFS              47
79 #define WEUA_ZideOne_Outlook   48
80
81 #define WEOS_UNKNOWN   0
82 #define WEOS_WINDOWS   1
83 #define WEOS_LINUX     2
84 #define WEOS_MACOS     3
85 #define WEOS_SUNOS     4
86
87 #define WECPU_UNKNOWN  0
88 #define WECPU_IX86     1
89 #define WECPU_SPARC    2
90 #define WECPU_PPC      3
91
92 @interface WEClientCapabilities(Privates)
93 - (id)initWithRequest:(WORequest *)_request;
94 @end
95
96 @implementation WEClientCapabilities
97
98 - (id)initWithRequest:(WORequest *)_request {
99   NSString *ac;
100   const char *ua;
101   const char *tmp;
102   int defaultOS  = WEOS_UNKNOWN;
103   int defaultCPU = WECPU_UNKNOWN;
104   
105   /* check charset */
106   
107   if ((ac = [_request headerForKey:@"accept-charset"])) {
108     /* not really correct ..., eg could have quality "0" ! */
109     ac = [ac lowercaseString];
110     if ([ac rangeOfString:@"utf-8"].length > 0)
111       self->flags.acceptUTF8 = 1;
112   }
113   
114   /* process user-agent */
115   
116   self->userAgent = [[_request headerForKey:@"user-agent"] copy];
117 #if LIB_FOUNDATION_LIBRARY
118   ua = [self->userAgent cString];
119 #else
120   ua = [self->userAgent UTF8String];
121 #endif
122   if (ua == NULL) {
123     /* no user-agent, eg telnet */
124     ua = "";
125   }
126   
127   /* detect browser */
128   
129   if ((tmp = strstr(ua, "Opera"))) {
130     /* Opera (can fake to be MSIE or Netscape) */
131     self->browser = WEUA_Opera;
132
133     /* go to next space */
134     while (!isspace(*tmp) && (*tmp != '\0')) tmp++;
135     /* skip spaces */
136     while (isspace(*tmp) && (*tmp != '\0')) tmp++;
137     
138     self->browserMajorVersion = atoi(tmp);
139     if ((tmp = index(tmp, '.'))) {
140       tmp++;
141       self->browserMinorVersion = atoi(tmp);
142     }
143   }
144   else if (strstr(ua, "NeonConnection") != NULL || 
145            strstr(ua, "ZIDEStore") != NULL ||
146            strstr(ua, "ZideLook-Codeon") != NULL) {
147     self->browser = WEUA_ZideLook;
148     self->browserMinorVersion = 0;
149     self->browserMajorVersion = 0;
150   }
151   else if (strstr(ua, "ZideOne/") != NULL) {
152     self->browser = WEUA_ZideOne_Outlook;
153     self->browserMinorVersion = 0;
154     self->browserMajorVersion = 0;
155   }
156   else if ((tmp = strstr(ua, "Safari/"))) {
157     /* Hm, Safari says it is a Mozilla/5.0 ? */
158     int combinedVersion;
159     self->browser = WEUA_Safari;
160     tmp += 7; /* skip "Safari/" */
161     combinedVersion = atoi(tmp);
162     /* well, don't know how this is supposed to work? 100=v1.1 */
163     if (combinedVersion == 100 /* 100 is v1.1 */) {
164       self->browserMajorVersion = 1;
165       self->browserMinorVersion = 1;
166     }
167     else {
168       /* watch for upcoming versions ... */
169       self->browserMajorVersion = combinedVersion / 100;
170     }
171   }
172   else if ((tmp = strstr(ua, "Sunbird/"))) {
173     /* Sunbird says it is a Mozilla */
174     self->browser = WEUA_Sunbird;
175     tmp += 8; /* skip "Sunbird/" */
176     
177     self->browserMajorVersion = atoi(tmp);
178     if ((tmp = index(tmp, '.'))) {
179       tmp++;
180       self->browserMinorVersion = atoi(tmp);
181     }
182   }
183   else if (strstr(ua, "Outlook-Express/")) {
184     /* Outlook Express 5.5 mailbox access via http */
185     self->browser = WEUA_MSOutlookExpress;
186   }
187   else if (strstr(ua, "Outlook Express/")) {
188     /* Outlook Express 5.0 mailbox access via http */
189     self->browser = WEUA_MSOutlookExpress;
190   }
191   else if (strstr(ua, "Microsoft-Outlook/")) {
192     /* Outlook 2002 mailbox access via http */
193     self->browser = WEUA_MSOutlook;
194   }
195   else if (strstr(ua, "Microsoft HTTP Post")) {
196     /* Outlook 2000 with WebPublishing Assistent */
197     self->browser = WEUA_MSWebPublisher;
198   }
199   else if (strstr(ua, "Entourage/10")) {
200     /* Entourage MacOSX 10.1.4 */
201     self->browser = WEUA_Entourage;
202   }
203   else if (strstr(ua, "Microsoft-WebDAV-MiniRedir/5")) {
204     /* WebFolders Win XP SP 2 */
205     self->browser = WEUA_WebFolder;
206   }
207   else if ((tmp = strstr(ua, "MSIE"))) {
208     /* Internet Explorer */
209     self->browser = WEUA_IE;
210     
211     /* go to next space */
212     while (!isspace(*tmp) && (*tmp != '\0')) tmp++;
213     /* skip spaces */
214     while (isspace(*tmp) && (*tmp != '\0')) tmp++;
215     
216     self->browserMajorVersion = atoi(tmp);
217     if ((tmp = index(tmp, '.'))) {
218       tmp++;
219       self->browserMinorVersion = atoi(tmp);
220     }
221   }
222   else if ((tmp = strstr(ua, "Konqueror"))) {
223     /* Konqueror (KDE2 FileManager) */
224     self->browser = WEUA_Konqueror;
225     
226     if ((tmp = index(tmp, '/'))) {
227       tmp++;
228       self->browserMajorVersion = atoi(tmp);
229       if ((tmp = index(tmp, '.'))) {
230         tmp++;
231         self->browserMinorVersion = atoi(tmp);
232       }
233     }
234   }
235   else if ((tmp = strstr(ua, "Netscape6"))) {
236     /* Netscape 6 */
237     self->browser = WEUA_Netscape;
238     
239     if ((tmp = index(tmp, '/'))) {
240       tmp++;
241       self->browserMajorVersion = atoi(tmp);
242       if ((tmp = index(tmp, '.'))) {
243         tmp++;
244         self->browserMinorVersion = atoi(tmp);
245       }
246     }
247   }
248   else if (strstr(ua, "Lynx")) {
249     /* Lynx */
250     self->browser = WEUA_Lynx;
251   }
252   else if (strstr(ua, "Links")) {
253     /* Links */
254     self->browser = WEUA_Links;
255   }
256   else if (strstr(ua, "gnome-vfs")) {
257     /* Links */
258     self->browser = WEUA_GNOMEVFS;
259   }
260   else if (strstr(ua, "cadaver")) {
261     /* Cadaver DAV browser */
262     self->browser = WEUA_CADAVER;
263   }
264   else if (strstr(ua, "GoLive")) {
265     /* Adobe GoLive */
266     self->browser = WEUA_GOLIVE;
267   }
268   else if (strstr(ua, "DAV.pm")) {
269     /* Perl HTTP::DAV */
270     self->browser = WEUA_PerlHTTPDAV;
271   }
272   else if (strstr(ua, "Darwin") != NULL && strstr(ua, "fetch/") != NULL) {
273     /* MacOSX 10.0 DAV FileSystem */
274     self->browser = WEUA_MACOSX_DAVFS;
275   }
276   else if (strstr(ua, "Darwin") != NULL && strstr(ua, "WebDAVFS/") != NULL) {
277     /* MacOSX DAV FileSystem */
278     self->browser = WEUA_MACOSX_DAVFS;
279   }
280   else if (strstr(ua, "OmniWeb")) {
281     /* OmniWeb */
282     self->browser = WEUA_OmniWeb;
283   }
284   else if (strstr(ua, "Evolution")) {
285     /* Evolution */
286     self->browser = WEUA_Evolution;
287   }
288   else if (strstr(ua, "wdfs/")) {
289     /* WDFS */
290     self->browser = WEUA_WDFS;
291   }
292   else if (strstr(ua, "Soup/")) {
293     /* SOUP (GNOME WebDAV library) */
294     self->browser = WEUA_SOUP;
295   }
296   else if (strstr(ua, "amaya")) {
297     /* W3C Amaya */
298     self->browser = WEUA_Amaya;
299   }
300   else if (strstr(ua, "NetNewsWire/")) {
301     /* NetNewsWire */
302     self->browser = WEUA_NetNewsWire;
303   }
304   else if (strstr(ua, "Dillo")) {
305     /* Dillo */
306     self->browser = WEUA_Dillo;
307   }
308   else if (strstr(ua, "Java")) {
309     /* Java SDK */
310     self->browser = WEUA_JavaSDK;
311   }
312   else if (strstr(ua, "Python-urllib")) {
313     /* Python URL module */
314     self->browser = WEUA_PythonURLLIB;
315   }
316   else if (strstr(ua, "xmlrpclib.py/")) {
317     /* Python XML-RPC module */
318     self->browser = WEUA_xmlrpclib_py;
319   }
320   else if (strstr(ua, "Emacs")) {
321     /* Emacs */
322     self->browser = WEUA_Emacs;
323   }
324   else if (strstr(ua, "iCab")) {
325     /* iCab ?? */
326     self->browser = WEUA_iCab;
327   }
328   else if (strstr(ua, "Wget")) {
329     /* Wget */
330     self->browser = WEUA_Wget;
331   }
332   else if (strstr(ua, "DAVAccess")) {
333     /* Apple MacOSX 10.2.1 / iCal 1.0 DAV Access Framework */
334     self->browser = WEUA_AppleDAVAccess;
335   }
336   else if (strstr(ua, "DAVKit/")) {
337     /* some iCal 1.x DAV Access Framework, report as Apple DAV access */
338     self->browser = WEUA_AppleDAVAccess;
339   }
340   else if (strstr(ua, "Microsoft Data Access Internet Publishing Provider")) {
341     /* WebFolder */
342     self->browser = WEUA_WebFolder;
343   }
344   else if (strstr(ua, "Microsoft Office Protocol Discovery")) {
345     /* Word 2003, treat as WebFolder */
346     self->browser = WEUA_WebFolder;
347   }
348   else if (strstr(ua, "curl")) {
349     /* curl program */
350     self->browser = WEUA_CURL;
351   }
352   else if (strstr(ua, "Mozilla")) {
353     /* other Netscape browser */
354     if (strstr(ua, "Mozilla/5")) {
355       self->browser = WEUA_Mozilla;
356       self->browserMajorVersion = 5;
357     }
358     else if (strstr(ua, "Mozilla/4")) {
359       self->browser = WEUA_Netscape;
360       self->browserMajorVersion = 4;
361     }
362     else {
363       NSLog(@"%s: Unknown Mozilla Browser: user-agent='%@'",
364             __PRETTY_FUNCTION__, self->userAgent);
365     }
366   }
367   else if (strstr(ua, "Morgul")) {
368     self->browser = WEUA_Morgul;
369   }
370   else if (strstr(ua, "WebDrive")) {
371     self->browser = WEUA_WebDrive;
372   }
373   else if (strstr(ua, "CFNetwork/1.1")) {
374     self->browser = WEUA_CFNetwork;
375   }
376   else if (strstr(ua, "Kung-Log/")) {
377     self->browser = WEUA_KungLog;
378   }
379   else if (strstr(ua, "ecto")) {
380     self->browser = WEUA_Ecto;
381   }
382   else if (strstr(ua, "NewsFire")) {
383     self->browser = WEUA_NewsFire;
384   }
385   else if (strstr(ua, "Goliath")) {
386     self->browser = WEUA_Goliath;
387   }
388   else if (strstr(ua, "SOPE/")) {
389     self->browser = WEUA_SOPE;
390   }
391   else if (strstr(ua, "Mediapartners-Google/")) {
392     self->browser = WEUA_Google;
393   }
394   else if (strstr(ua, "PEAR XML_RPC")) {
395     self->browser = WEUA_PEAR_XMLRPC;
396   }
397   else if (strstr(ua, "XML-RPC.NET")) {
398     self->browser = WEUA_Cook_XMLRPCdotNET;
399   }
400   else {
401     /* unknown browser */
402     self->browser = WEUA_UNKNOWN;
403     
404     if (self->userAgent) {
405       NSLog(@"%s: Unknown WebClient: user-agent='%@'",
406             __PRETTY_FUNCTION__, self->userAgent);
407     }
408   }
409   
410   /* detect OS */
411
412   if (strstr(ua, "Windows") != NULL || strstr(ua, "WinNT") != NULL)
413     self->os = WEOS_WINDOWS;
414   else if (strstr(ua, "Linux"))
415     self->os = WEOS_LINUX;
416   else if (strstr(ua, "Mac"))
417     self->os = WEOS_MACOS;
418   else if (strstr(ua, "SunOS"))
419     self->os = WEOS_SUNOS;
420   else
421     self->os = defaultOS;
422
423   /* detect CPU */
424
425   if (strstr(ua, "sun4u"))
426     self->cpu = WECPU_SPARC;
427   else if (strstr(ua, "i686") || strstr(ua, "i586"))
428     self->cpu = WECPU_IX86;
429   else if (strstr(ua, "PowerPC") || strstr(ua, "ppc") || strstr(ua, "PPC"))
430     self->cpu = WECPU_PPC;
431   else if (self->os == WEOS_WINDOWS)
432     /* assume ix86 if OS is Windows .. */
433     self->cpu = WECPU_IX86;
434   else 
435     self->cpu = defaultCPU;
436   
437   return self;
438 }
439
440 - (void)dealloc {
441   [self->userAgent release];
442   [super dealloc];
443 }
444
445 /* accessors */
446
447 - (NSString *)userAgent {
448   return self->userAgent;
449 }
450
451 - (NSString *)userAgentType {
452   switch (self->browser) {
453     case WEUA_IE:                return @"IE";
454     case WEUA_Netscape:          return @"Netscape";
455     case WEUA_Lynx:              return @"Lynx";
456     case WEUA_Links:             return @"Links";
457     case WEUA_Opera:             return @"Opera";
458     case WEUA_Amaya:             return @"Amaya";
459     case WEUA_Emacs:             return @"Emacs";
460     case WEUA_Wget:              return @"Wget";
461     case WEUA_WebFolder:         return @"WebFolder";
462     case WEUA_DAVFS:             return @"DAVFS";
463     case WEUA_MACOSX_DAVFS:      return @"MacOSXDAVFS";
464     case WEUA_CADAVER:           return @"Cadaver";
465     case WEUA_GOLIVE:            return @"GoLive";
466     case WEUA_Mozilla:           return @"Mozilla";
467     case WEUA_OmniWeb:           return @"OmniWeb";
468     case WEUA_iCab:              return @"iCab";
469     case WEUA_Konqueror:         return @"Konqueror";
470     case WEUA_Dillo:             return @"Dillo";
471     case WEUA_JavaSDK:           return @"Java";
472     case WEUA_PythonURLLIB:      return @"Python-urllib";
473     case WEUA_AppleDAVAccess:    return @"AppleDAVAccess";
474     case WEUA_MSWebPublisher:    return @"MSWebPublisher";
475     case WEUA_CURL:              return @"CURL";
476     case WEUA_Evolution:         return @"Evolution";
477     case WEUA_SOUP:              return @"SOUP";
478     case WEUA_MSOutlook:         return @"MSOutlook";
479     case WEUA_MSOutlookExpress:  return @"MSOutlookExpress";
480     case WEUA_GNOMEVFS:          return @"GNOME-VFS";
481     case WEUA_ZideLook:          return @"ZideLook";
482     case WEUA_Safari:            return @"Safari";
483     case WEUA_Entourage:         return @"Entourage";
484     case WEUA_NetNewsWire:       return @"NetNewsWire";
485     case WEUA_xmlrpclib_py:      return @"xmlrpclib.py";
486     case WEUA_Morgul:            return @"Morgul";
487     case WEUA_KungLog:           return @"KungLog";
488     case WEUA_Ecto:              return @"Ecto";
489     case WEUA_NewsFire:          return @"NewsFire";
490     case WEUA_Goliath:           return @"Goliath";
491     case WEUA_PerlHTTPDAV:       return @"PerlHTTPDAV";
492     case WEUA_Google:            return @"Google";
493     case WEUA_WebDrive:          return @"WebDrive";
494     case WEUA_Sunbird:           return @"Sunbird";
495     case WEUA_PEAR_XMLRPC:       return @"PHP PEAR XMLRPC";
496     case WEUA_Cook_XMLRPCdotNET: return @"PHP PEAR XMLRPC";
497     case WEUA_WDFS:              return @"WDFS";
498     case WEUA_ZideOne_Outlook:   return @"ZideOne";
499     default:                     return @"unknown";
500   }
501 }
502 - (NSString *)os {
503   switch (self->os) {
504     case WEOS_WINDOWS: return @"Windows";
505     case WEOS_LINUX:   return @"Linux";
506     case WEOS_MACOS:   return @"MacOS";
507     case WEOS_SUNOS:   return @"SunOS";
508     default:           return @"unknown";
509   }
510 }
511 - (NSString *)cpu {
512   switch (self->cpu) {
513     case WECPU_IX86:  return @"ix86";
514     case WECPU_SPARC: return @"sparc";
515     case WECPU_PPC:   return @"ppc";
516     default:          return @"unknown";
517   }
518 }
519
520 - (unsigned char)majorVersion {
521   return self->browserMajorVersion;
522 }
523 - (unsigned char)minorVersion {
524   return self->browserMinorVersion;
525 }
526
527 /* browser capabilities */
528
529 - (BOOL)isJavaScriptBrowser {
530   switch (self->browser) {
531     case WEUA_Mozilla:
532     case WEUA_IE:
533     case WEUA_Opera:
534     case WEUA_Netscape:
535     case WEUA_OmniWeb:
536     case WEUA_Konqueror:
537     case WEUA_Safari:
538       return YES;
539       
540     default:
541       return NO;
542   }
543 }
544 - (BOOL)isVBScriptBrowser {
545   switch (self->browser) {
546     case WEUA_IE:
547       return YES;
548     
549     default:
550       return NO;
551   }
552 }
553
554 - (BOOL)isFastTableBrowser {
555   switch (self->browser) {
556     case WEUA_Mozilla:
557     case WEUA_IE:
558     case WEUA_Opera:
559       return YES;
560
561     case WEUA_Safari:
562     case WEUA_Konqueror:
563       /* to be tried */
564       return YES;
565       
566     case WEUA_Netscape:
567       return (self->browserMajorVersion >= 6)
568         ? YES : NO;
569       
570     default:
571       return NO;
572   }
573 }
574
575 - (BOOL)isCSS2Browser {
576   switch (self->browser) {
577     case WEUA_IE:        return (self->browserMajorVersion >= 5) ? YES : NO;
578     case WEUA_Netscape:  return (self->browserMajorVersion >= 6) ? YES : NO;
579     case WEUA_Opera:     return (self->browserMajorVersion >= 4) ? YES : NO;
580     case WEUA_Mozilla:   return YES;
581     case WEUA_Safari:    return YES;
582     case WEUA_Konqueror: return NO;
583     default:             return NO;
584   }
585 }
586
587 - (BOOL)isCSS1Browser {
588   switch (self->browser) {
589     case WEUA_IE:        return (self->browserMajorVersion >= 4) ? YES : NO;
590     case WEUA_Netscape:  return (self->browserMajorVersion >= 4) ? YES : NO;
591     case WEUA_Opera:     return (self->browserMajorVersion >= 4) ? YES : NO;
592     case WEUA_Safari:    return YES;
593     case WEUA_Konqueror: return NO;
594     default:             return NO;
595   }
596 }
597
598 - (BOOL)ignoresCSSOnFormElements {
599   if (self->browser == WEUA_Safari) /* Safari always displays Aqua buttons */
600     return YES;
601   
602   return [self isCSS1Browser] ? NO : YES;
603 }
604
605 - (BOOL)isXULBrowser {
606   if (self->browser == WEUA_Safari) // TODO: Safari supports some XUL stuff
607     return NO;
608   if ((self->browser == WEUA_Netscape) && (self->browserMajorVersion >= 6))
609     return YES;
610   if (self->browser == WEUA_Mozilla)
611     return YES;
612   return NO;
613 }
614
615 - (BOOL)isTextModeBrowser {
616   if (self->browser == WEUA_Lynx)  return YES;
617   if (self->browser == WEUA_Links) return YES;
618   if (self->browser == WEUA_Emacs) return YES;
619   return NO;
620 }
621
622 - (BOOL)isIFrameBrowser {
623   if ((self->browser == WEUA_IE) && (self->browserMajorVersion >= 5))
624     return YES;
625   
626   /* as suggested in OGo bug #634 */
627   if ((self->browser == WEUA_Mozilla) && (self->browserMajorVersion >= 5))
628     return YES;
629   
630   return NO;
631 }
632
633 - (BOOL)isRobot {
634   if (self->browser == WEUA_Wget)         return YES;
635   if (self->browser == WEUA_JavaSDK)      return YES;
636   if (self->browser == WEUA_PythonURLLIB) return YES;
637   if (self->browser == WEUA_Google)       return YES;
638   return NO;
639 }
640 - (BOOL)isDAVClient {
641   if (self->browser == WEUA_WebFolder)        return YES;
642   if (self->browser == WEUA_DAVFS)            return YES;
643   if (self->browser == WEUA_MACOSX_DAVFS)     return YES;
644   if (self->browser == WEUA_CADAVER)          return YES;
645   if (self->browser == WEUA_GOLIVE)           return YES;
646   if (self->browser == WEUA_AppleDAVAccess)   return YES;
647   if (self->browser == WEUA_Evolution)        return YES;
648   if (self->browser == WEUA_SOUP)             return YES;
649   if (self->browser == WEUA_MSOutlook)        return YES;
650   if (self->browser == WEUA_MSOutlookExpress) return YES;
651   if (self->browser == WEUA_GNOMEVFS)         return YES;
652   if (self->browser == WEUA_ZideLook)         return YES;
653   if (self->browser == WEUA_Entourage)        return YES;
654   if (self->browser == WEUA_Morgul)           return YES;
655   if (self->browser == WEUA_Goliath)          return YES;
656   if (self->browser == WEUA_PerlHTTPDAV)      return YES;
657   if (self->browser == WEUA_WebDrive)         return YES;
658   if (self->browser == WEUA_Sunbird)          return YES;
659   if (self->browser == WEUA_WDFS)             return YES;
660   if (self->browser == WEUA_ZideOne_Outlook)  return YES;
661   return NO;
662 }
663
664 - (BOOL)isXmlRpcClient {
665   if (self->browser == WEUA_xmlrpclib_py) return YES;
666   if (self->browser == WEUA_KungLog)      return YES;
667   if (self->browser == WEUA_Ecto)         return YES;
668   if (self->browser == WEUA_PEAR_XMLRPC)  return YES;
669   if (self->browser == WEUA_Cook_XMLRPCdotNET) return YES;
670   return NO;
671 }
672 - (BOOL)isBLogClient {
673   if (self->browser == WEUA_KungLog) return YES;
674   if (self->browser == WEUA_Ecto)    return YES;
675   return NO;
676 }
677 - (BOOL)isRSSClient {
678   if (self->browser == WEUA_NetNewsWire) return YES;
679   if (self->browser == WEUA_NewsFire)    return YES;
680   return NO;
681 }
682
683 - (BOOL)doesSupportCSSOverflow {
684   if (![self isCSS1Browser])
685     return NO;
686   if ((self->browser == WEUA_IE) && (self->browserMajorVersion >= 5))
687     return YES;
688
689   return NO;
690 }
691
692 - (BOOL)doesSupportDHTMLDragAndDrop {
693   if (![self isJavaScriptBrowser])
694     return NO;
695   if (self->os != WEOS_WINDOWS)
696     return NO;
697   if ((self->browser == WEUA_IE) && (self->browserMajorVersion >= 5))
698     return YES;
699   return NO;
700 }
701
702 - (BOOL)doesSupportXMLDataIslands {
703   if ((self->browser == WEUA_IE) && (self->browserMajorVersion >= 5))
704     return YES;
705   return NO;
706 }
707
708 - (BOOL)doesSupportUTF8Encoding {
709   if (self->flags.acceptUTF8)
710     /* explicit UTF-8 support signaled in HTTP header */
711     return YES;
712   
713   switch (self->browser) {
714   case WEUA_Mozilla:
715   case WEUA_Safari:
716   case WEUA_ZideLook:
717   case WEUA_Evolution:
718   case WEUA_SOUP:
719   case WEUA_Morgul:
720   case WEUA_Sunbird:
721   case WEUA_ZideOne_Outlook:
722     /* browser so new, that they always supported UTF-8 ... */
723     return YES;
724   case WEUA_IE:
725     if (self->browserMajorVersion >= 5)
726       return YES;
727     return NO; // TODO: find out, whether IE 4 gurantees UTF-8 support
728   default:
729     return NO;
730   }
731 }
732
733 /* user-agent */
734
735 - (BOOL)isInternetExplorer {
736   return self->browser == WEUA_IE ? YES : NO;
737 }
738 - (BOOL)isInternetExplorer5 {
739   return (self->browser == WEUA_IE) && (self->browserMajorVersion == 5)
740     ? YES : NO;
741 }
742
743 - (BOOL)isNetscape {
744   return self->browser == WEUA_Netscape ? YES : NO;
745 }
746 - (BOOL)isNetscape6 {
747   return (self->browser == WEUA_Netscape) && (self->browserMajorVersion == 6)
748     ? YES : NO;
749 }
750
751 - (BOOL)isLynx {
752   return self->browser == WEUA_Lynx ? YES : NO;
753 }
754 - (BOOL)isOpera {
755   return self->browser == WEUA_Opera ? YES : NO;
756 }
757 - (BOOL)isAmaya {
758   return self->browser == WEUA_Amaya ? YES : NO;
759 }
760 - (BOOL)isEmacs {
761   return self->browser == WEUA_Emacs ? YES : NO;
762 }
763 - (BOOL)isWget {
764   return self->browser == WEUA_Wget ? YES : NO;
765 }
766 - (BOOL)isWebFolder {
767   return self->browser == WEUA_WebFolder ? YES : NO;
768 }
769 - (BOOL)isMozilla {
770   return self->browser == WEUA_Mozilla ? YES : NO;
771 }
772 - (BOOL)isOmniWeb {
773   return self->browser == WEUA_OmniWeb ? YES : NO;
774 }
775 - (BOOL)isICab {
776   return self->browser == WEUA_iCab ? YES : NO;
777 }
778 - (BOOL)isKonqueror {
779   return self->browser == WEUA_Konqueror ? YES : NO;
780 }
781 - (BOOL)isPHP {
782   return self->browser == WEUA_PEAR_XMLRPC ? YES : NO;
783 }
784
785 /* OS */
786
787 - (BOOL)isWindowsBrowser {
788   return self->os == WEOS_WINDOWS ? YES : NO;
789 }
790 - (BOOL)isLinuxBrowser {
791   return self->os == WEOS_LINUX ? YES : NO;
792 }
793 - (BOOL)isMacBrowser {
794   return self->os == WEOS_MACOS ? YES : NO;
795 }
796 - (BOOL)isSunOSBrowser {
797   return self->os == WEOS_SUNOS ? YES : NO;
798 }
799 - (BOOL)isUnixBrowser {
800   switch (self->os) {
801     case WEOS_SUNOS:
802     case WEOS_LINUX:
803       return YES;
804     default: return NO;
805   }
806 }
807 - (BOOL)isX11Browser {
808   if ([self isTextModeBrowser])
809     return NO;
810   if (![self isUnixBrowser])
811     return NO;
812   return YES;
813 }
814
815 /* NSCopying */
816
817 - (id)copyWithZone:(NSZone *)_zone {
818   return [self retain];
819 }
820
821 /* description */
822
823 - (NSString *)description {
824   NSMutableString *s;
825
826   s = [NSMutableString stringWithFormat:@"<%@[0x%p]:",
827                          NSStringFromClass([self class]), self];
828   
829   //[s appendFormat:@" ua='%@'", self->userAgent];
830   [s appendFormat:@" type=%@ v%i.%i",
831        [self userAgentType],
832        self->browserMajorVersion, self->browserMinorVersion];
833   [s appendFormat:@" os=%@",   [self os]];
834   [s appendFormat:@" cpu=%@",  [self cpu]];
835   
836   if ([self isFastTableBrowser])  [s appendString:@" fast-tbl"];
837   if ([self isCSS1Browser])       [s appendString:@" css1"];
838   if ([self isCSS2Browser])       [s appendString:@" css2"];
839   if ([self isXULBrowser])        [s appendString:@" xul"];
840   if ([self isTextModeBrowser])   [s appendString:@" text"];
841   if ([self isRobot])             [s appendString:@" robot"];
842   if ([self isJavaScriptBrowser]) [s appendString:@" js"];
843   if ([self isVBScriptBrowser])   [s appendString:@" vb"];
844   
845   [s appendString:@">"];
846   return s;
847 }
848
849 @end /* WEClientCapabilities */
850
851 static NSString *ClientCapsCacheKey = @"WEClientCapabilities";
852
853 @implementation WORequest(ClientCapabilities)
854
855 - (WEClientCapabilities *)clientCapabilities {
856   NSDictionary         *ua;
857   WEClientCapabilities *ccaps;
858   NSMutableDictionary  *md;
859
860   if ((ua = [self userInfo]) == nil) {
861     ccaps = [WEClientCapabilities alloc];
862     if ((ccaps = [ccaps initWithRequest:self]) == nil)
863       return nil;
864     ccaps = [ccaps autorelease];
865     
866     ua = [[NSDictionary alloc] initWithObjects:&ccaps
867                                forKeys:&ClientCapsCacheKey
868                                count:1];
869     [self setUserInfo:ua];
870     [ua release];
871     return ccaps;
872   }
873   
874   if ((ccaps = [ua objectForKey:ClientCapsCacheKey]))
875     return ccaps;
876   
877   ccaps = [WEClientCapabilities alloc];
878   if ((ccaps = [ccaps initWithRequest:self]) == nil)
879     return nil;
880   ccaps = [ccaps autorelease];
881     
882   md = [ua mutableCopy];
883   [md setObject:ccaps forKey:ClientCapsCacheKey];
884   ua = [md copy];
885   [md release];
886   [self setUserInfo:ua];
887   [ua release];
888   return ccaps;
889 }
890
891 @end /* WORequest(ClientCapabilities) */
892
893 static NSString *WEClientDetectorFormName = @"WEClientDetect";
894
895 @implementation JSClientCapabilityDetector
896
897 - (id)initWithName:(NSString *)_name
898   associations:(NSDictionary *)_config
899   template:(WOElement *)_c
900 {
901   if ((self = [super initWithName:_name associations:_config template:_c])) {
902     self->formName   = OWGetProperty(_config, @"formName");
903     self->clientCaps = OWGetProperty(_config, @"clientCaps");
904   }
905   return self;
906 }
907
908 - (void)dealloc {
909   [self->formName   release];
910   [self->clientCaps release];
911   [super dealloc];
912 }
913
914 - (NSString *)_formNameInContext:(WOContext *)_ctx {
915   if (self->formName)
916     return [self->formName stringValueInComponent:[_ctx component]];
917   return nil;
918 }
919
920 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
921   if (![_ctx isInForm]) {
922     [[_ctx component]
923            warnWithFormat:@"you must use %@ inside a form !",
924              NSStringFromClass([self class])];
925     return;
926   }
927
928   if ([_ctx isRenderingDisabled]) return;
929
930   if (![[[_ctx request] clientCapabilities] isJavaScriptBrowser])
931     /* only works on JavaScript browsers ... */
932     return;
933
934   [_response appendContentString:@"<input type='hidden' name='"];
935   [_response appendContentString:WEClientDetectorFormName];
936   [_response appendContentString:@"' value='browserConfig' />"];
937   
938   [_response appendContentString:@"<script language='JavaScript'>\n"];
939   [_response appendContentString:@"<!-- hide\n"];
940   
941   [_response appendContentString:@"// -->\n"];
942   [_response appendContentString:@"</script>"];
943 }
944
945 @end /* JSClientCapabilityDetector */
946
947 /*
948   Netscape 4.76, Windows NT 4
949     'Mozilla/4.76 [en] (WinNT; U)'
950   
951   Netscape 6, Windows NT 4
952     'Mozilla/5.0 (Windows; U; WinNT4.0; en-US; m18) Gecko/20001108 Netscape6/6.0'
953   
954   Netscape Navigator 3.01[de], MacOS 8.1
955     'Mozilla/3.01 [de]-C-MACOS8 (Macintosh; I; PPC)'
956
957   Netscape Communicator 4.51, SuSE Linux 6.1
958     'Mozilla/4.51 [en] (X11; I; Linux 2.2.13 i686)'
959   
960   Mozilla M17, Windows NT 4
961     'Mozilla/5.0 (Windows; U; WinNT4.0; en-US; m17) Gecko/20000807'
962   
963   Internet Explorer 5.5, Windows NT 4
964     'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0)'
965   
966   Internet Explorer 3.0.1, MacOS 8.1
967     'Mozilla/3.0 (compatible; MSIE 3.0.1; Mac_PowerPC; Mac OS8)'
968     
969   Internet Explorer 5.0, MacOS 8.1
970     'Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC)'
971
972   Internet Explorer 5.0, SPARC Solaris 2.6 (trex)
973     'Mozilla/4.0 (compatible; MSIE 5.0; SunOS 5.6 sun4u; X11)'
974   
975   Konqueror/2.0, SuSE Linux 7.0
976     'Mozilla/5.0 (compatible; Konqueror/2.0; X11); Supports MD5-Digest; Supports gzip encoding'
977   
978   Lynx, SuSE Linux 6.1 (marvin)
979     'Lynx/2.8rel.2 libwww-FM/2.14'
980
981   Lynx, SPARC Solaris 2.6 (trex)
982     'Lynx/2.7 libwww-FM/2.14'
983   
984   Opera 4.02, Windows NT 4
985     'Mozilla/4.73 (Windows NT 4.0; U) Opera 4.02  [en]'
986   
987   Opera 5.0, Windows NT 4
988     'Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 4.0) Opera 5.0  [en]'
989   
990   Amaya 4.0, Windows NT 4
991     'amaya/V4.0 libwww/5.3.1'
992   
993   XEmacs, SuSE Linux 6.1
994     'Emacs-W3/4.0pre.39 URL/p4.0pre.39 (i686-pc-linux; X11)'
995
996   XEmacs, SuSE Linux 7.2
997     'Emacs-W3/4.0pre.46 URL/p4.0pre.46 (i386-suse-linux; X11)'
998   
999   wget, SuSE Linux 6.1
1000     'Wget/1.5.3'
1001   
1002   Windows 'WebFolder' NT4
1003     'Microsoft Data Access Internet Publishing Provider Cache Manager'
1004     'Mozilla/2.0 (compatible; MS FrontPage 4.0)'
1005     'MSFrontPage/4.0'
1006
1007   Windows 98 IE 5 WebFolders
1008     'Microsoft Data Access Internet Publishing Provider DAV'
1009
1010   OmniWeb
1011     'OmniWeb/3.0.2 OWF/1999C'
1012
1013   Links, SuSE Linux 6.1 (marvin)
1014     'Links (0.95; Linux 2.2.13 i686)'
1015
1016   Linux DAVFS
1017     'DAV-FS/0.0.1'
1018
1019   MacOSX 10.0 DAVFS
1020     fetch/1.0 Darwin/1.3.7 (Power Macintosh)
1021
1022   MacOSX 10.1.1 DAV FS
1023     WebDAVFS/1.0 Darwin/5.1 (Power Macintosh)
1024   
1025   MacOSX 10.2.1 DAV FS
1026     WebDAVFS/1.2.1 (01218000) Darwin/6.1 (Power Macintosh)
1027
1028   MacOSX 10.4.2 DAV FS
1029     WebDAVFS/1.4.1 (01418000) Darwin/8.6.0 (Power Macintosh)
1030   
1031   Cadaver 0.17.0
1032     'cadaver/0.17.0 neon/0.12.0-dev'
1033   
1034   Adobe GoLive 5
1035     'GoLive/5. 0 [] (Windows 98; RATBERT)'
1036
1037   Dillo 0.6.2
1038     - (very) small X11 web browser
1039     'Dillo/0.6.1'
1040   
1041   Java SDK 1.3
1042     'Java1.3.0'
1043
1044   Python 2.0
1045     'Python-urllib/1.13'
1046   
1047   Apple MacOSX 10.2.1 / iCal 1.0 DAV Access Framework
1048     'DAVAccess/1.0'
1049   
1050   Outlook 2000 on W2K with M$ Web Publishing Assistent
1051     'Microsoft HTTP Post (RFC1867)'
1052   
1053   CURL program (libcurl)
1054     'curl/7.9.8 (i686-suse-linux) libcurl 7.9.8 (OpenSSL 0.9.6g) (ipv6 enabled)'
1055
1056   Evolution 1.0.8 with Exchange Connector (WebDAV)
1057     'Evolution/1.0.8'
1058   
1059   Outlook 2002 on W2K (HotMail HTTP access)
1060     'Microsoft-Outlook/10.0 (TmstmpExt)'
1061   
1062   Outlook Express 5.5 on W2K (HotMail HTTP access)
1063     'Outlook-Express/5.5 (MSIE 5.5; Windows NT 5.0; Q312461; T312461; TmstmpExt)'
1064   
1065   Outlook Express 6.0 on W2K (HotMail HTTP access)
1066     'Outlook-Express/6.0 (MSIE 6.0; Windows NT 5.0; Q312461; T312461; TmstmpExt)'
1067
1068   Nautilus (GNOME Virtual Filesystem)
1069     'gnome-vfs/1.0.5'
1070   
1071   Konqueror 3.0.3 (SuSE 8.1)
1072     - does not send a user-agent in webdav:// mode !
1073
1074   ZideLook 0.0
1075     'neon/0.23.5 NeonConnection 0.0'
1076
1077   Safari v74 (MacOSX 10.2)
1078     'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/74 (KHTML, like Gecko) Safari/74'
1079     - why does it say Mozilla/5.0 ??
1080
1081   Evolution 1.4.0 with Exchange Connector 1.4.0 (SuSE 8.2)
1082     'Evolution/1.4.0'
1083     
1084   Mozilla Firebird 0.6 (MacOSX 10.2)
1085     'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6'
1086
1087   SOUP (Evo OGo Connector by Anders)
1088     'Soup/1.99.24'
1089
1090   Entourage/X (Entourage WebDAV 10.1.4, MacOSX)
1091     'Entourage/10.0 (Mac_PowerPC; DigExt; TmstmpExt)'
1092   
1093   Some unknown iCal.app
1094     'DAVKit/0.1'
1095
1096   NetNewsWire full version: 
1097     'NetNewsWire/1.0.5 (Mac OS X; http://ranchero.com/netnewswire/)'
1098   
1099   NetNewsWire lite version: 
1100     'NetNewsWire/1.0.3 (Mac OS X; Lite; http://ranchero.com/netnewswire/)'
1101
1102   Python xmlrpclib:
1103     'xmlrpclib.py/1.0.0 (by www.pythonware.com)'
1104
1105   Windows 2000 IE 6 WebFolders
1106     'Microsoft Data Access Internet Publishing Provider DAV 1.1'
1107
1108   Morgul, Windows WebDAV client
1109     'Morgul'
1110
1111   Apple iSync v122 / CoreFoundation Network
1112     'CFNetwork/1.1'
1113
1114   Kung-Log (WebServicesCore)
1115     'Kung-Log/1.3 (Macintosh; U; PPC Mac OS X) WebServicesCore'
1116
1117   Ecto (WebServicesCore)
1118     'ecto (Macintosh; U; PPC Mac OS X) WebServicesCore'
1119
1120   NewsFire
1121     'NewsFire/0.23'
1122   
1123   Goliath
1124     'Goliath/1.0.1 (Macintosh-Carbon; PPC)'
1125
1126   PERL HTTP::DAV
1127     'DAV.pm/v0.31'
1128   
1129   Google Ads
1130     'Mediapartners-Google/2.1'
1131
1132   WebFolders Win XP SP2
1133     'Microsoft-WebDAV-MiniRedir/5.1.2600'
1134   
1135   Word 2003
1136     'Microsoft Office Protocol Discovery'
1137
1138   WebDrive
1139     'WebDrive 7.10.1475 DAV'
1140
1141   Nokia N80, Opera 8 (352x416, 24b)
1142     'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Symbian OS; Series 60/; 7439) Opera 8.60 [de]'
1143     'X-OS-Prefs: fw:352; fh:416; cd:24c; pl:3; pj:0; pa:1;pi:0;ps:0;'
1144
1145   Nokia N80, Safari
1146     'User-Agent: Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413'
1147
1148   Nokia N80, WAP Browser
1149     'User-Agent: NokiaN80-1/3.0 (3.0611.0.8) Series60/3.0  Profile/MIDP-2.0 Configuration/CLDC-1.1'
1150     'x-wap-profile: "http://nds1.nds.nokia.com/uaprof/NN80-1r100.xml"'
1151
1152   Sony/Ericsson T610 (128x160, 16b)
1153     'user-agent: SonyEricssonT610/R301 Profile/MIDP-1.0 Configuration/CLDC-1.0 UP.Link/6.3.0.0.0'
1154     'x-up-devcap-charset: US-ASCII,ISO-8859-1,UTF-8,ISO-10646-UCS-2'
1155     'x-up-devcap-iscolor: 1'
1156     'x-up-devcap-max-pdu: 10000'
1157     'x-up-devcap-screendepth: 16'
1158     'x-up-devcap-screenpixels: 128,160'
1159     'x-up-forwarded-for: 10.233.155.62'
1160     'x-up-subno: 981574289-60174629'
1161     'x-up-wtls-info: off'
1162     'x-wap-profile: "http://wap.sonyericsson.com/UAprof/T610R301.xml"'
1163
1164   WDFS
1165     'wdfs/1.4.2 neon/0.26.4'
1166 */