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