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