KIO
script.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (c) 2003 Malte Starostik <malte@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "script.h" 00021 00022 00023 #include <cstdlib> 00024 #include <vector> 00025 #include <algorithm> 00026 #include <ctime> 00027 #include <cstring> 00028 00029 #include <netdb.h> 00030 #include <sys/types.h> 00031 #include <netinet/in.h> 00032 #include <arpa/inet.h> 00033 #include <unistd.h> 00034 00035 #include <QtCore/QRegExp> 00036 #include <QtCore/QString> 00037 #include <QtNetwork/QHostAddress> 00038 #include <QtNetwork/QHostInfo> 00039 00040 #include <kurl.h> 00041 #include <kjs/object.h> 00042 #include <kjs/JSVariableObject.h> 00043 00044 using namespace KJS; 00045 00046 QString UString::qstring() const 00047 { 00048 return QString( reinterpret_cast< const QChar* >( data() ), size() ); 00049 } 00050 00051 UString::UString( const QString &s ) 00052 { 00053 const unsigned int len = s.length(); 00054 UChar *data = static_cast<UChar*>( fastMalloc( sizeof(UChar) * len ) ); 00055 memcpy( data, s.unicode(), len * sizeof( UChar ) ); 00056 m_rep = Rep::create( data, len ); 00057 } 00058 00059 namespace 00060 { 00061 class Address 00062 { 00063 public: 00064 struct Error {}; 00065 static Address resolve( const UString& host ) 00066 { return Address( host.qstring(), false ); } 00067 static Address parse( const UString& ip ) 00068 { return Address( ip.qstring(), true ); } 00069 00070 operator QHostAddress() const { return m_address; } 00071 operator UString() const { return UString( m_address.toString() ); } 00072 00073 private: 00074 Address( const QString& host, bool numeric ) 00075 { 00076 if ( numeric ) { 00077 m_address = QHostAddress( host ); 00078 if ( m_address.isNull() ) 00079 throw Error(); 00080 } else { 00081 QHostInfo addresses = QHostInfo::fromName(host); 00082 if ( addresses.error() || addresses.addresses().isEmpty() ) 00083 throw Error(); 00084 m_address = addresses.addresses().at(0); 00085 } 00086 } 00087 00088 QHostAddress m_address; 00089 }; 00090 00091 struct Function : public JSObject 00092 { 00093 struct ResolveError {}; 00094 00095 virtual bool implementsCall() const { return true; } 00096 00097 static int findString( const UString& s, const char* const* values ) 00098 { 00099 int index = 0; 00100 UString lower = s.qstring().toLower(); 00101 for ( const char* const* p = values; *p; ++p, ++index ) 00102 if ( lower == *p ) return index; 00103 return -1; 00104 } 00105 00106 static const tm* getTime( ExecState* exec, const List& args ) 00107 { 00108 time_t now = std::time( 0 ); 00109 if ( args[ args.size() - 1 ]->toString( exec ).qstring().toLower() == "gmt" ) 00110 return std::gmtime( &now ); 00111 else return std::localtime( &now ); 00112 } 00113 00114 JSValue *checkRange( double value, double min, double max ) 00115 { 00116 return jsBoolean(( min <= max && value >= min && value <= max ) || ( min > max && ( value <= min || value >= max ) )); 00117 } 00118 }; 00119 00120 // isPlainHostName( host ) 00121 // @returns true if @p host doesn't contains a domain part 00122 struct IsPlainHostName : public Function 00123 { 00124 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00125 { 00126 if ( args.size() != 1 ) return jsUndefined(); 00127 return jsBoolean( args[ 0 ]->toString( exec ).qstring().indexOf( "." ) == -1 ); 00128 } 00129 }; 00130 00131 // dnsDomainIs( host, domain ) 00132 // @returns true if the domain part of @p host matches @p domain 00133 struct DNSDomainIs : public Function 00134 { 00135 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00136 { 00137 if ( args.size() != 2 ) return jsUndefined(); 00138 QString host = args[ 0 ]->toString( exec ).qstring().toLower(); 00139 QString domain = args[ 1 ]->toString( exec ).qstring().toLower(); 00140 return jsBoolean( host.endsWith( domain ) ); 00141 } 00142 }; 00143 00144 // localHostOrDomainIs( host, fqdn ) 00145 // @returns true if @p host is unqualified or equals @p fqdn 00146 struct LocalHostOrDomainIs : public Function 00147 { 00148 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00149 { 00150 if ( args.size() != 2 ) return jsUndefined(); 00151 UString host = args[ 0 ]->toString( exec ).qstring().toLower(); 00152 if ( host.find( "." ) == -1 ) return jsBoolean( true ); 00153 UString fqdn = args[ 1 ]->toString( exec ).qstring().toLower(); 00154 return jsBoolean( host == fqdn ); 00155 } 00156 }; 00157 00158 // isResolvable( host ) 00159 // @returns true if host can be resolved via DNS 00160 struct IsResolvable : public Function 00161 { 00162 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00163 { 00164 if ( args.size() != 1 ) return jsUndefined(); 00165 try { Address::resolve( args[ 0 ]->toString( exec ) ); } 00166 catch ( const Address::Error& ) { return jsBoolean( false ); } 00167 return jsBoolean( true ); 00168 } 00169 }; 00170 00171 // isInNet( host, subnet, mask ) 00172 // @returns true if @p host is within the IP subnet 00173 // specified via @p subnet and @p mask 00174 struct IsInNet : public Function 00175 { 00176 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00177 { 00178 if ( args.size() != 3 ) return jsUndefined(); 00179 try 00180 { 00181 QHostAddress host = Address::resolve( args[ 0 ]->toString( exec ) ); 00182 QHostAddress subnet = Address::parse( args[ 1 ]->toString( exec ) ); 00183 QHostAddress mask = Address::parse( args[ 2 ]->toString( exec ) ); 00184 00185 return jsBoolean( ( host.toIPv4Address() & mask.toIPv4Address() ) == 00186 ( subnet.toIPv4Address() & mask.toIPv4Address() ) ); 00187 } 00188 catch ( const Address::Error& ) 00189 { 00190 return jsUndefined(); 00191 } 00192 } 00193 }; 00194 00195 // dnsResolve( host ) 00196 // @returns the IP address of @p host in dotted quad notation 00197 struct DNSResolve : public Function 00198 { 00199 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00200 { 00201 if ( args.size() != 1 ) return jsUndefined(); 00202 try { return jsString(Address::resolve( args[ 0 ]->toString( exec ) )); } 00203 catch ( const Address::Error& ) { return jsUndefined(); } 00204 } 00205 }; 00206 00207 // myIpAddress() 00208 // @returns the local machine's IP address in dotted quad notation 00209 struct MyIpAddress : public Function 00210 { 00211 virtual JSValue *callAsFunction( ExecState*, JSObject*, const List& args ) 00212 { 00213 if ( args.size() ) return jsUndefined(); 00214 char hostname[ 256 ]; 00215 gethostname( hostname, 255 ); 00216 hostname[ 255 ] = 0; 00217 try { return jsString(Address::resolve( hostname )); } 00218 catch ( const Address::Error& ) { return jsUndefined(); } 00219 } 00220 }; 00221 00222 // dnsDomainLevels( host ) 00223 // @returns the number of dots ('.') in @p host 00224 struct DNSDomainLevels : public Function 00225 { 00226 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00227 { 00228 if ( args.size() != 1 ) return jsUndefined(); 00229 UString host = args[ 0 ]->toString( exec ); 00230 if ( host.isNull() ) return jsNumber( 0 ); 00231 #ifdef __SUNPRO_CC 00232 /* 00233 * Under Solaris, the default STL is the old broken interface 00234 * to ::count which takes an extra Size& parameter. 00235 */ 00236 int c = 0; 00237 std::count( host.data(), host.data() + host.size(), '.', c ); 00238 return jsNumber(c); 00239 #else 00240 return jsNumber( std::count( 00241 host.data(), host.data() + host.size(), '.' ) ); 00242 #endif 00243 } 00244 }; 00245 00246 // shExpMatch( str, pattern ) 00247 // @returns true if @p str matches the shell @p pattern 00248 struct ShExpMatch : public Function 00249 { 00250 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00251 { 00252 if ( args.size() != 2 ) return jsUndefined(); 00253 QRegExp pattern( args[ 1 ]->toString( exec ).qstring(), Qt::CaseSensitive, QRegExp::Wildcard ); 00254 return jsBoolean( pattern.exactMatch(args[ 0 ]->toString( exec ).qstring()) ); 00255 } 00256 }; 00257 00258 // weekdayRange( day [, "GMT" ] ) 00259 // weekdayRange( day1, day2 [, "GMT" ] ) 00260 // @returns true if the current day equals day or between day1 and day2 resp. 00261 // If the last argument is "GMT", GMT timezone is used, otherwise local time 00262 struct WeekdayRange : public Function 00263 { 00264 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00265 { 00266 if ( args.size() < 1 || args.size() > 3 ) return jsUndefined(); 00267 static const char* const days[] = 00268 { "sun", "mon", "tue", "wed", "thu", "fri", "sat", 0 }; 00269 int d1 = findString( args[ 0 ]->toString( exec ), days ); 00270 if ( d1 == -1 ) return jsUndefined(); 00271 00272 int d2 = findString( args[ 1 ]->toString( exec ), days ); 00273 if ( d2 == -1 ) d2 = d1; 00274 return checkRange( getTime( exec, args )->tm_wday, d1, d2 ); 00275 } 00276 }; 00277 00278 // dateRange( day [, "GMT" ] ) 00279 // dateRange( day1, day2 [, "GMT" ] ) 00280 // dateRange( month [, "GMT" ] ) 00281 // dateRange( month1, month2 [, "GMT" ] ) 00282 // dateRange( year [, "GMT" ] ) 00283 // dateRange( year1, year2 [, "GMT" ] ) 00284 // dateRange( day1, month1, day2, month2 [, "GMT" ] ) 00285 // dateRange( month1, year1, month2, year2 [, "GMT" ] ) 00286 // dateRange( day1, month1, year1, day2, month2, year2 [, "GMT" ] ) 00287 // @returns true if the current date (GMT or local time according to 00288 // presence of "GMT" as last argument) is within the given range 00289 struct DateRange : public Function 00290 { 00291 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00292 { 00293 if ( args.size() < 1 || args.size() > 7 ) return jsUndefined(); 00294 static const char* const months[] = 00295 { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "nov", "dec", 0 }; 00296 00297 std::vector< double > values; 00298 for ( int i = 0; i < args.size(); ++i ) 00299 { 00300 double value = -1; 00301 if ( args[ i ]->type() == NumberType ) 00302 value = args[ i ]->toInteger( exec ); 00303 else value = findString( args[ i ]->toString( exec ), months ); 00304 if ( value >= 0 ) values.push_back( value ); 00305 else break; 00306 } 00307 00308 const tm* now = getTime( exec, args ); 00309 00310 // day1, month1, year1, day2, month2, year2 00311 if ( values.size() == 6 ) 00312 return checkRange( ( now->tm_year + 1900 ) * 372 + now->tm_mon * 31 + now->tm_mday, 00313 values[ 2 ] * 372 + values[ 1 ] * 31 + values[ 0 ], 00314 values[ 5 ] * 372 + values[ 4 ] * 31 + values[ 3 ] ); 00315 00316 // day1, month1, day2, month2 00317 else if ( values.size() == 4 && 00318 values[ 1 ] < 12 && 00319 values[ 3 ] < 12 ) 00320 return checkRange( now->tm_mon * 31 + now->tm_mday, 00321 values[ 1 ] * 31 + values[ 0 ], 00322 values[ 3 ] * 31 + values[ 2 ] ); 00323 00324 // month1, year1, month2, year2 00325 else if ( values.size() == 4 ) 00326 return checkRange( ( now->tm_year + 1900 ) * 12 + now->tm_mon, 00327 values[ 1 ] * 12 + values[ 0 ], 00328 values[ 3 ] * 12 + values[ 2 ] ); 00329 00330 // year1, year2 00331 else if ( values.size() == 2 && 00332 values[ 0 ] >= 1000 && 00333 values[ 1 ] >= 1000 ) 00334 return checkRange( now->tm_year + 1900, values[ 0 ], values[ 1 ] ); 00335 00336 // day1, day2 00337 else if ( values.size() == 2 && 00338 args[ 0 ]->type() == NumberType && 00339 args[ 1 ]->type() == NumberType ) 00340 return checkRange( now->tm_mday, values[ 0 ], values[ 1 ] ); 00341 00342 // month1, month2 00343 else if ( values.size() == 2 ) 00344 return checkRange( now->tm_mon, values[ 0 ], values[ 1 ] ); 00345 00346 // year 00347 else if ( values.size() == 1 && values[ 0 ] >= 1000 ) 00348 return checkRange( now->tm_year + 1900, values[ 0 ], values[ 0 ] ); 00349 00350 // day 00351 else if ( values.size() == 1 && args[ 0 ]->type() == NumberType ) 00352 return checkRange( now->tm_mday, values[ 0 ], values[ 0 ] ); 00353 00354 // month 00355 else if ( values.size() == 1 ) 00356 return checkRange( now->tm_mon, values[ 0 ], values[ 0 ] ); 00357 00358 else return jsUndefined(); 00359 } 00360 }; 00361 00362 // timeRange( hour [, "GMT" ] ) 00363 // timeRange( hour1, hour2 [, "GMT" ] ) 00364 // timeRange( hour1, min1, hour2, min2 [, "GMT" ] ) 00365 // timeRange( hour1, min1, sec1, hour2, min2, sec2 [, "GMT" ] ) 00366 // @returns true if the current time (GMT or local based on presence 00367 // of "GMT" argument) is within the given range 00368 struct TimeRange : public Function 00369 { 00370 virtual JSValue *callAsFunction( ExecState* exec, JSObject*, const List& args ) 00371 { 00372 if ( args.size() < 1 || args.size() > 7 ) return jsUndefined(); 00373 00374 std::vector< double > values; 00375 for ( int i = 0; i < args.size(); ++i ) 00376 if ( args[ i ]->type() == NumberType ) 00377 values.push_back( args[ i ]->toInteger( exec ) ); 00378 else break; 00379 00380 const tm* now = getTime( exec, args ); 00381 00382 // hour1, min1, sec1, hour2, min2, sec2 00383 if ( values.size() == 6 ) 00384 return checkRange( now->tm_hour * 3600 + now->tm_min * 60 + now->tm_sec, 00385 values[ 0 ] * 3600 + values[ 1 ] * 60 + values[ 2 ], 00386 values[ 3 ] * 3600 + values[ 4 ] * 60 + values[ 5 ] ); 00387 00388 // hour1, min1, hour2, min2 00389 else if ( values.size() == 4 ) 00390 return checkRange( now->tm_hour * 60 + now->tm_min, 00391 values[ 0 ] * 60 + values[ 1 ], 00392 values[ 2 ] * 60 + values[ 3 ] ); 00393 00394 // hour1, hour2 00395 else if ( values.size() == 2 ) 00396 return checkRange( now->tm_hour, values[ 0 ], values[ 1 ] ); 00397 00398 // hour 00399 else if ( values.size() == 1 ) 00400 return checkRange( now->tm_hour, values[ 0 ], values[ 0 ] ); 00401 00402 else return jsUndefined(); 00403 } 00404 }; 00405 00406 void registerFunctions( ExecState* exec, JSObject *global ) 00407 { 00408 global->put( exec, "isPlainHostName", new IsPlainHostName ); 00409 global->put( exec, "dnsDomainIs", new DNSDomainIs ); 00410 global->put( exec, "localHostOrDomainIs", new LocalHostOrDomainIs ); 00411 global->put( exec, "isResolvable", new IsResolvable ); 00412 global->put( exec, "isInNet", new IsInNet ); 00413 global->put( exec, "dnsResolve", new DNSResolve ); 00414 global->put( exec, "myIpAddress", new MyIpAddress ); 00415 global->put( exec, "dnsDomainLevels", new DNSDomainLevels ); 00416 global->put( exec, "shExpMatch", new ShExpMatch ); 00417 global->put( exec, "weekdayRange", new WeekdayRange ); 00418 global->put( exec, "dateRange", new DateRange ); 00419 global->put( exec, "timeRange", new TimeRange ); 00420 } 00421 } 00422 00423 namespace KPAC 00424 { 00425 Script::Script( const QString& code ) 00426 { 00427 m_interpreter = new KJS::Interpreter(); 00428 m_interpreter->ref(); 00429 ExecState* exec = m_interpreter->globalExec(); 00430 JSObject* global = m_interpreter->globalObject(); 00431 registerFunctions( exec, global ); 00432 00433 Completion result = m_interpreter->evaluate( "", 0, code ); 00434 if ( result.complType() == Throw ) 00435 throw Error( result.value()->toString( exec ).qstring() ); 00436 } 00437 00438 Script::~Script() 00439 { 00440 m_interpreter->deref(); 00441 } 00442 00443 QString Script::evaluate( const KUrl& url ) 00444 { 00445 ExecState *exec = m_interpreter->globalExec(); 00446 JSValue *findFunc = m_interpreter->globalObject()->get( exec, "FindProxyForURL" ); 00447 JSObject *findObj = findFunc->getObject(); 00448 if (!findObj || !findObj->implementsCall()) 00449 throw Error( "No such function FindProxyForURL" ); 00450 00451 List args; 00452 args.append(jsString(url.url())); 00453 args.append(jsString(url.host())); 00454 JSValue *retval = findObj->call( exec, m_interpreter->globalObject(), args ); 00455 00456 if ( exec->hadException() ) { 00457 JSValue *ex = exec->exception(); 00458 exec->clearException(); 00459 throw Error( ex->toString( exec ).qstring() ); 00460 } 00461 00462 return retval->toString( exec ).qstring(); 00463 } 00464 } 00465 00466 // vim: ts=4 sw=4 et
KDE 4.6 API Reference