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