nntp.tcl

Go to the documentation of this file.
00001 /*  nntp.tcl --*/
00002 /* */
00003 /*        nntp implementation for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 1998-2000 by Ajuba Solutions.*/
00006 /*  All rights reserved.*/
00007 /*  */
00008 /*  RCS: @(#) $Id: nntp.tcl,v 1.13 2004/05/03 22:56:25 andreas_kupries Exp $*/
00009 
00010 package require Tcl 8.2
00011 package provide nntp 0.2.1
00012 
00013 namespace ::nntp {
00014     /*  The socks variable holds the handle to the server connections*/
00015     variable socks
00016 
00017     /*  The counter is used to help create unique connection names*/
00018     variable counter 0
00019 
00020     /*  commands is the list of subcommands recognized by nntp*/
00021     variable commands [list \
00022             "article"     \
00023             "authinfo"    \
00024             "body"        \
00025             "date"        \
00026             "group"       \
00027             "head"        \
00028             "help"        \
00029             "last"        \
00030             "list"        \
00031             "listgroup"   \
00032             "mode_reader" \
00033             "newgroups"   \
00034             "newnews"     \
00035             "next"        \
00036             "post"        \
00037             "stat"        \
00038             "quit"        \
00039             "xgtitle"     \
00040             "xhdr"        \
00041             "xover"       \
00042             "xpat"        \
00043             ]
00044 
00045      ::nntp = ::eol "\n"
00046 
00047     /*  only export one command, the one used to instantiate a new*/
00048     /*  nntp connection */
00049     namespace export nntp
00050 
00051 }
00052 
00053 /*  ::nntp::nntp --*/
00054 /* */
00055 /*        Create a new nntp connection.*/
00056 /* */
00057 /*  Arguments:*/
00058 /*         server -   The name of the nntp server to connect to (optional).*/
00059 /*         port -     The port number to connect to (optional).*/
00060 /*         name -     The name of the nntp connection to create (optional).*/
00061 /* */
00062 /*  Results:*/
00063 /*     Creates a connection to the a nntp server.  By default the*/
00064 /*     connection is established with the machine 'news' at port '119'*/
00065 /*     These defaults can be overridden with the environment variables*/
00066 /*     NNTPPORT and NNTPHOST, or can be passed as optional arguments*/
00067 
00068 ret  ::nntp::nntp (optional server ="" , optional port ="" , optional name ="") {
00069     global env
00070     variable connections
00071     variable counter
00072     variable socks
00073 
00074     # If a name wasn't specified for the connection, create a new 'unique'
00075     # name for the connection 
00076 
00077     if { [llength [info level 0]] < 4 } {
00078         set counter 0
00079         set name "nntp${counter}"
00080         while {[lsearch -exact [info commands] $name] >= 0} {
00081             incr counter
00082             set name "nntp${counter}"
00083         }
00084     }
00085 
00086     if { ![string equal [info commands ::$name] ""] } {
00087         error "command \"$name\" already exists, unable to create nntp connection"
00088     }
00089 
00090     upvar 0 ::nntp::${name}data data
00091 
00092     set socks($name) [list ]
00093 
00094     # Initialize instance specific variables
00095 
00096     set data(debug) 0
00097     set data(eol) "\n"
00098 
00099     # Logic to determine whether to use the specified nntp server, or to use
00100     # the default
00101 
00102     if {$server == ""} {
00103         if {[info exists env(NNTPSERVER)]} {
00104             set data(host) "$env(NNTPSERVER)"
00105         } else {
00106             set data(host) "news"
00107         }
00108     } else {
00109         set data(host) $server
00110     }
00111 
00112     # Logic to determine whether to use the specified nntp port, or to use the
00113     # default.
00114 
00115     if {$port == ""} {
00116         if {[info exists env(NNTPPORT)]} {
00117             set data(port) $env(NNTPPORT)
00118         } else {    
00119             set data(port) 119
00120         }
00121     } else {
00122         set data(port) $port
00123     }
00124  
00125     set data(code) 0
00126     set data(mesg) ""
00127     set data(addr) ""
00128     set data(binary) 0
00129 
00130     set sock [socket $data(host) $data(port)]
00131 
00132     set data(sock) $sock
00133 
00134     # Create the command to manipulate the nntp connection
00135 
00136     interp alias {} ::$name {} ::nntp::NntpProc $name
00137     
00138     ::nntp::response $name
00139 
00140     return $name
00141 }
00142 
00143 /*  ::nntp::NntpProc --*/
00144 /* */
00145 /*        Command that processes all nntp object commands.*/
00146 /* */
00147 /*  Arguments:*/
00148 /*        name    name of the nntp object to manipulate.*/
00149 /*        args    command name and args for the command.*/
00150 /* */
00151 /*  Results:*/
00152 /*        Calls the appropriate nntp procedure for the command specified in*/
00153 /*        'args' and passes 'args' to the command/procedure.*/
00154 
00155 ret  ::nntp::NntpProc (type name , optional cmd ="" , type args) {
00156 
00157     # Do minimal args checks here
00158 
00159     if { [llength [info level 0]] < 3 } {
00160         error "wrong # args: should be \"$name option ?arg arg ...?\""
00161     }
00162 
00163     # Split the args into command and args components
00164 
00165     if { [llength [info commands ::nntp::_$cmd]] == 0 } {
00166         variable commands
00167         set optlist [join $commands ", "]
00168         set optlist [linsert $optlist "end-1" "or"]
00169         error "bad option \"$cmd\": must be $optlist"
00170     }
00171 
00172     # Call the appropriate command with its arguments
00173 
00174     return [eval [linsert $args 0 ::nntp::_$cmd $name]]
00175 }
00176 
00177 /*  ::nntp::okprint --*/
00178 /* */
00179 /*        Used to test the return code stored in data(code) to*/
00180 /*        make sure that it is alright to right to the socket.*/
00181 /* */
00182 /*  Arguments:*/
00183 /*        name    name of the nntp object.*/
00184 /* */
00185 /*  Results:*/
00186 /*        Either throws an error describing the failure, or*/
00187 /*        'args' and passes 'args' to the command/procedure or*/
00188 /*        returns 1 for 'OK' and 0 for error states.   */
00189 
00190 ret  ::nntp::okprint (type name) {
00191     upvar 0 ::nntp::${name}data data
00192 
00193     if {$data(code) >=400} {
00194         set val [expr {(0 < $data(code)) && ($data(code) < 400)}]
00195         error "NNTPERROR: $data(code) $data(mesg)"
00196     }
00197 
00198     # Codes less than 400 are good
00199 
00200     return [expr {(0 < $data(code)) && ($data(code) < 400)}]
00201 }
00202 
00203 /*  ::nntp::message --*/
00204 /* */
00205 /*        Used to format data(mesg) for printing to the socket*/
00206 /*        by appending the appropriate end of line character which*/
00207 /*        is stored in data(eol).*/
00208 /* */
00209 /*  Arguments:*/
00210 /*        name    name of the nntp object.*/
00211 /* */
00212 /*  Results:*/
00213 /*        Returns a string containing the message from data(mesg) followed*/
00214 /*        by the eol character(s) stored in data(eol)*/
00215 
00216 ret  ::nntp::message (type name) {
00217     upvar 0 ::nntp::${name}data data
00218 
00219     return "$data(mesg)$data(eol)"
00220 }
00221 
00222 /* */
00223 /* */
00224 /*  NNTP Methods*/
00225 /* */
00226 
00227 ret  ::nntp::_cget (type name , type option) {
00228     upvar 0 ::nntp::${name}data data
00229 
00230     if {[string equal $option -binary]} {
00231     return $data(binary)
00232     } else {
00233     return -code error \
00234         "Illegal option \"$option\", expected \"-binary\""
00235     }
00236 }
00237 
00238 ret  ::nntp::_configure (type name , type args) {
00239     upvar 0 ::nntp::${name}data data
00240 
00241     if {[llength $args] == 0} {
00242     return [list -binary $data(binary)]
00243     }
00244     if {[llength $args] == 1} {
00245     return [_cget $name [lindex $args 0]]
00246     }
00247     if {([llength $args] % 2) == 1} {
00248     return -code error \
00249         "wrong#args: expected even number of elements"
00250     }
00251     foreach {o v} $args {
00252     if {[string equal $o -binary]} {
00253         if {![string is boolean -strict $v]} {
00254         return -code error \
00255             "Expected boolean, got \"$v\""
00256         }
00257         set data(binary) $v
00258     } else {
00259         return -code error \
00260             "Illegal option \"$o\", expected \"-binary\""
00261     }
00262     }
00263     return {}
00264 }
00265 
00266 
00267 /*  ::nntp::_article --*/
00268 /* */
00269 /*        Internal article proc.  Called by the 'nntpName article' command.*/
00270 /*        Retrieves the article specified by msgid, in the group specified by*/
00271 /*        the 'nntpName group' command.  If no msgid is specified the current */
00272 /*        (or first) article in the group is retrieved*/
00273 /* */
00274 /*  Arguments:*/
00275 /*        name    name of the nntp object.*/
00276 /*        msgid   The article number to retrieve*/
00277 /* */
00278 /*  Results:*/
00279 /*        Returns the message (if there is one) from the specified group as*/
00280 /*        a valid tcl list where each element is a line of the message.*/
00281 /*        If no article is found, the "" string is returned.*/
00282 /* */
00283 /*  According to RFC 977 the responses are:*/
00284 /* */
00285 /*    220 n  article retrieved - head and body follow*/
00286 /*            (n = article number,  = message-id)*/
00287 /*    221 n  article retrieved - head follows*/
00288 /*    222 n  article retrieved - body follows*/
00289 /*    223 n  article retrieved - request text separately*/
00290 /*    412 no newsgroup has been selected*/
00291 /*    420 no current article has been selected*/
00292 /*    423 no such article number in this group*/
00293 /*    430 no such article found*/
00294 /* */
00295  
00296 ret  ::nntp::_article (type name , optional msgid ="") {
00297     upvar 0 ::nntp::${name}data data
00298 
00299     set data(cmnd) "fetch"
00300     return [::nntp::command $name "ARTICLE $msgid"]
00301 }
00302 
00303 /*  ::nntp::_authinfo --*/
00304 /* */
00305 /*        Internal authinfo proc.  Called by the 'nntpName authinfo' command.*/
00306 /*        Passes the username and password for a nntp server to the nntp server. */
00307 /* */
00308 /*  Arguments:*/
00309 /*        name    Name of the nntp object.*/
00310 /*        user    The username for the nntp server.*/
00311 /*        pass    The password for 'username' on the nntp server.*/
00312 /* */
00313 /*  Results:*/
00314 /*        Returns the result of the attempts to set the username and password*/
00315 /*        on the nntp server ( 1 if successful, 0 if failed).*/
00316 
00317 ret  ::nntp::_authinfo (type name , optional user ="guest" , optional pass ="foobar") {
00318     upvar 0 ::nntp::${name}data data
00319 
00320     set data(cmnd) ""
00321     set res [::nntp::command $name "AUTHINFO USER $user"]
00322     if {$res} {
00323         set res [expr {$res && [::nntp::command $name "AUTHINFO PASS $pass"]}]
00324     }
00325     return $res
00326 }
00327 
00328 /*  ::nntp::_body --*/
00329 /* */
00330 /*        Internal body proc.  Called by the 'nntpName body' command.*/
00331 /*        Retrieves the body of the article specified by msgid from the group*/
00332 /*        specified by the 'nntpName group' command. If no msgid is specified*/
00333 /*        the current (or first) message body is returned  */
00334 /* */
00335 /*  Arguments:*/
00336 /*        name    Name of the nntp object.*/
00337 /*        msgid   The number of the body of the article to retrieve*/
00338 /* */
00339 /*  Results:*/
00340 /*        Returns the body of article 'msgid' from the group specified through*/
00341 /*        'nntpName group'. If msgid is not specified or is "" then the body of*/
00342 /*        the current (or the first) article in the newsgroup will be returned */
00343 /*        as a valid tcl list.  The "" string will be returned if there is no*/
00344 /*        article 'msgid' or if no group has been specified.*/
00345 
00346 ret  ::nntp::_body (type name , optional msgid ="") {
00347     upvar 0 ::nntp::${name}data data
00348 
00349     set data(cmnd) "fetch"
00350     return [::nntp::command $name "BODY $msgid"]
00351 }
00352 
00353 /*  ::nntp::_group --*/
00354 /* */
00355 /*        Internal group proc.  Called by the 'nntpName group' command.*/
00356 /*        Sets the current group on the nntp server to the group passed in.*/
00357 /* */
00358 /*  Arguments:*/
00359 /*        name    Name of the nntp object.*/
00360 /*        group   The name of the group to set as the default group.*/
00361 /* */
00362 /*  Results:*/
00363 /*     Sets the default group to the group specified. If no group is specified*/
00364 /*     or if an invalid group is specified an error is thrown.*/
00365 /* */
00366 /*  According to RFC 977 the responses are:*/
00367 /* */
00368 /*   211 n f l s group selected*/
00369 /*            (n = estimated number of articles in group,*/
00370 /*            f = first article number in the group,*/
00371 /*            l = last article number in the group,*/
00372 /*            s = name of the group.)*/
00373 /*   411 no such news group*/
00374 
00375 ret  ::nntp::_group (type name , optional group ="") {
00376     upvar 0 ::nntp::${name}data data
00377 
00378     set data(cmnd) "groupinfo"
00379     if {$group == ""} {
00380         set group $data(group)
00381     }
00382     return [::nntp::command $name "GROUP $group"]
00383 }
00384 
00385 /*  ::nntp::_head --*/
00386 /* */
00387 /*        Internal head proc.  Called by the 'nntpName head' command.*/
00388 /*        Retrieves the header of the article specified by msgid from the group*/
00389 /*        specified by the 'nntpName group' command. If no msgid is specified*/
00390 /*        the current (or first) message header is returned  */
00391 /* */
00392 /*  Arguments:*/
00393 /*        name    Name of the nntp object.*/
00394 /*        msgid   The number of the header of the article to retrieve*/
00395 /* */
00396 /*  Results:*/
00397 /*        Returns the header of article 'msgid' from the group specified through*/
00398 /*        'nntpName group'. If msgid is not specified or is "" then the header of*/
00399 /*        the current (or the first) article in the newsgroup will be returned */
00400 /*        as a valid tcl list.  The "" string will be returned if there is no*/
00401 /*        article 'msgid' or if no group has been specified.*/
00402 
00403 ret  ::nntp::_head (type name , optional msgid ="") {
00404     upvar 0 ::nntp::${name}data data
00405 
00406     set data(cmnd) "fetch"
00407     return [::nntp::command $name "HEAD $msgid"]
00408 }
00409 
00410 /*  ::nntp::_help --*/
00411 /* */
00412 /*        Internal help proc.  Called by the 'nntpName help' command.*/
00413 /*        Retrieves a list of the valid nntp commands accepted by the server.*/
00414 /* */
00415 /*  Arguments:*/
00416 /*        name    Name of the nntp object.*/
00417 /* */
00418 /*  Results:*/
00419 /*        Returns the NNTP commands expected by the NNTP server.*/
00420 
00421 ret  ::nntp::_help (type name) {
00422     upvar 0 ::nntp::${name}data data
00423 
00424     set data(cmnd) "fetch"
00425     return [::nntp::command $name "HELP"]
00426 }
00427 
00428 ret  ::nntp::_ihave (type name , optional msgid ="" , type args) {
00429     upvar 0 ::nntp::${name}data data
00430 
00431     set data(cmnd) "fetch"
00432     if {![::nntp::command $name "IHAVE $msgid"]} {
00433         return ""
00434     }
00435     return [::nntp::squirt $name "$args"]    
00436 }
00437 
00438 /*  ::nntp::_last --*/
00439 /* */
00440 /*        Internal last proc.  Called by the 'nntpName last' command.*/
00441 /*        Sets the current message to the message before the current message.*/
00442 /* */
00443 /*  Arguments:*/
00444 /*        name    Name of the nntp object.*/
00445 /* */
00446 /*  Results:*/
00447 /*        None.*/
00448 
00449 ret  ::nntp::_last (type name) {
00450     upvar 0 ::nntp::${name}data data
00451 
00452     set data(cmnd) "msgid"
00453     return [::nntp::command $name "LAST"]
00454 }
00455 
00456 /*  ::nntp::_list --*/
00457 /* */
00458 /*        Internal list proc.  Called by the 'nntpName list' command.*/
00459 /*        Lists all groups or (optionally) all groups of a specified type.*/
00460 /* */
00461 /*  Arguments:*/
00462 /*        name    Name of the nntp object.*/
00463 /*        Type    The type of groups to return (active active.times newsgroups*/
00464 /*                distributions distrib.pats moderators overview.fmt*/
00465 /*                subscriptions) - optional.*/
00466 /* */
00467 /*  Results:*/
00468 /*        Returns a tcl list of all groups or the groups that match 'type' if*/
00469 /*        a type is specified.*/
00470 
00471 ret  ::nntp::_list (type name , optional type ="") {
00472     upvar 0 ::nntp::${name}data data
00473 
00474     set data(cmnd) "fetch"
00475     return [::nntp::command $name "LIST $type"]
00476 }
00477 
00478 /*  ::nntp::_newgroups --*/
00479 /* */
00480 /*        Internal newgroups proc.  Called by the 'nntpName newgroups' command.*/
00481 /*        Lists all new groups since a specified time.*/
00482 /* */
00483 /*  Arguments:*/
00484 /*        name    Name of the nntp object.*/
00485 /*        since   The time to find new groups since.  The time can be in any*/
00486 /*                format that is accepted by 'clock scan' in tcl.*/
00487 /* */
00488 /*  Results:*/
00489 /*        Returns a tcl list of all new groups added since the time specified. */
00490 
00491 ret  ::nntp::_newgroups (type name , type since , type args) {
00492     upvar 0 ::nntp::${name}data data
00493 
00494     set since [clock format [clock scan "$since"] -format "%y%m%d %H%M%S"]
00495     set dist ""
00496     set data(cmnd) "fetch"
00497     return [::nntp::command $name "NEWGROUPS $since $dist"]
00498 }
00499 
00500 /*  ::nntp::_newnews --*/
00501 /* */
00502 /*        Internal newnews proc.  Called by the 'nntpName newnews' command.*/
00503 /*        Lists all new news in the specified group since a specified time.*/
00504 /* */
00505 /*  Arguments:*/
00506 /*        name    Name of the nntp object.*/
00507 /*        group   Name of the newsgroup to query.*/
00508 /*        since   The time to find new groups since.  The time can be in any*/
00509 /*                format that is accepted by 'clock scan' in tcl. Defaults to*/
00510 /*                "1 day ago"*/
00511 /* */
00512 /*  Results:*/
00513 /*        Returns a tcl list of all new messages since the time specified. */
00514 
00515 ret  ::nntp::_newnews (type name , optional group ="" , optional since ="") {
00516     upvar 0 ::nntp::${name}data data
00517 
00518     if {$group != ""} {
00519         if {[regexp -- {^[\w\.\-]+$} $group] == 0} {
00520             set since $group
00521             set group ""
00522         }
00523     }
00524     if {![info exists group] || ($group == "")} {
00525         if {[info exists data(group)] && ($data(group) != "")} {
00526             set group $data(group)
00527         } else {
00528             set group "*"
00529         }
00530     }
00531     if {"$since" == ""} {
00532         set since [clock format [clock scan "now - 1 day"]]
00533     }
00534     set since [clock format [clock scan $since] -format "%y%m%d %H%M%S"]
00535     set dist "" 
00536     set data(cmnd) "fetch"
00537     return [::nntp::command $name "NEWNEWS $group $since $dist"]
00538 }
00539 
00540 /*  ::nntp::_next --*/
00541 /* */
00542 /*        Internal next proc.  Called by the 'nntpName next' command.*/
00543 /*        Sets the current message to the next message after the current message.*/
00544 /* */
00545 /*  Arguments:*/
00546 /*        name    Name of the nntp object.*/
00547 /* */
00548 /*  Results:*/
00549 /*        None.*/
00550 
00551 ret  ::nntp::_next (type name) {
00552     upvar 0 ::nntp::${name}data data
00553 
00554     set data(cmnd) "msgid"
00555     return [::nntp::command $name "NEXT"]
00556 }
00557 
00558 /*  ::nntp::_post --*/
00559 /* */
00560 /*        Internal post proc.  Called by the 'nntpName post' command.*/
00561 /*        Posts a message to a newsgroup.*/
00562 /* */
00563 /*  Responses (according to RFC 977) to a post request:*/
00564 /*   240 article posted ok*/
00565 /*   340 send article to be posted. End with .*/
00566 /*   440 posting not allowed*/
00567 /*   441 posting failed*/
00568 /* */
00569 /*  Arguments:*/
00570 /*        name    Name of the nntp object.*/
00571 /*        article A message of the form specified in RFC 850*/
00572 /* */
00573 /*  Results:*/
00574 /*        None.*/
00575 
00576 ret  ::nntp::_post (type name , type article) {
00577     
00578     if {![::nntp::command $name "POST"]} {
00579         return ""
00580     }
00581     return [::nntp::squirt $name "$article"]
00582 }
00583 
00584 /*  ::nntp::_slave --*/
00585 /* */
00586 /*        Internal slave proc.  Called by the 'nntpName slave' command.*/
00587 /*        Identifies a connection as being made from a slave nntp server.*/
00588 /*        This might be used to indicate that the connection is serving*/
00589 /*        multiple people and should be given priority.  Actual use is */
00590 /*        entirely implementation dependant and may vary from server to*/
00591 /*        server.*/
00592 /* */
00593 /*  Arguments:*/
00594 /*        name    Name of the nntp object.*/
00595 /* */
00596 /*  Results:*/
00597 /*        None.*/
00598 /* */
00599 /*  According to RFC 977 the only response is:*/
00600 /* */
00601 /*     202 slave status noted*/
00602 
00603 ret  ::nntp::_slave (type name) {
00604     return [::nntp::command $name "SLAVE"]
00605 }
00606 
00607 /*  ::nntp::_stat --*/
00608 /* */
00609 /*        Internal stat proc.  Called by the 'nntpName stat' command.*/
00610 /*        The stat command is similar to the article command except that no*/
00611 /*        text is returned.  When selecting by message number within a group,*/
00612 /*        the stat command serves to set the current article pointer without*/
00613 /*        sending text. The returned acknowledgement response will contain the*/
00614 /*        message-id, which may be of some value.  Using the stat command to*/
00615 /*        select by message-id is valid but of questionable value, since a*/
00616 /*        selection by message-id does NOT alter the "current article pointer"*/
00617 /* */
00618 /*  Arguments:*/
00619 /*        name    Name of the nntp object.*/
00620 /*        msgid   The number of the message to stat (optional) default is to*/
00621 /*                stat the current article*/
00622 /* */
00623 /*  Results:*/
00624 /*        Returns the statistics for the article.*/
00625 
00626 ret  ::nntp::_stat (type name , optional msgid ="") {
00627     upvar 0 ::nntp::${name}data data
00628 
00629     set data(cmnd) "status"
00630     return [::nntp::command $name "STAT $msgid"]
00631 }
00632 
00633 /*  ::nntp::_quit --*/
00634 /* */
00635 /*        Internal quit proc.  Called by the 'nntpName quit' command.*/
00636 /*        Quits the nntp session and closes the socket.  Deletes the command*/
00637 /*        that was created for the connection.*/
00638 /* */
00639 /*  Arguments:*/
00640 /*        name    Name of the nntp object.*/
00641 /* */
00642 /*  Results:*/
00643 /*        Returns the return value from the quit command.*/
00644 
00645 ret  ::nntp::_quit (type name) {
00646     upvar 0 ::nntp::${name}data data
00647 
00648     set ret [::nntp::command $name "QUIT"]
00649     close $data(sock)
00650     rename ${name} {}
00651     return $ret
00652 }
00653 
00654 /* */
00655 /* */
00656 /*  Extended methods (not available on all NNTP servers*/
00657 /* */
00658 
00659 ret  ::nntp::_date (type name) {
00660     upvar 0 ::nntp::${name}data data
00661 
00662     set data(cmnd) "msg"
00663     return [::nntp::command $name "DATE"]
00664 }
00665 
00666 ret  ::nntp::_listgroup (type name , optional group ="") {
00667     upvar 0 ::nntp::${name}data data
00668 
00669     set data(cmnd) "fetch"
00670     return [::nntp::command $name "LISTGROUP $group"]
00671 }
00672 
00673 ret  ::nntp::_mode_reader (type name) {
00674     upvar 0 ::nntp::${name}data data
00675 
00676     set data(cmnd) "msg"
00677     return [::nntp::command $name "MODE READER"]
00678 }
00679 
00680 ret  ::nntp::_xgtitle (type name , optional group_pattern ="") {
00681     upvar 0 ::nntp::${name}data data
00682 
00683     set data(cmnd) "fetch"
00684     return [::nntp::command $name "XGTITLE $group_pattern"]
00685 }
00686 
00687 ret  ::nntp::_xhdr (type name , optional header ="message-id" , optional list ="" , optional last ="") {
00688     upvar 0 ::nntp::${name}data data
00689 
00690     if {![regexp -- {\d+-\d+} $list]} {
00691         if {"$last" != ""} {
00692             set list "$list-$last"
00693         } else {
00694             set list ""
00695     }
00696     }
00697     set data(cmnd) "fetch"
00698     return [::nntp::command $name "XHDR $header $list"]    
00699 }
00700 
00701 ret  ::nntp::_xindex (type name , optional group ="") {
00702     upvar 0 ::nntp::${name}data data
00703 
00704     if {("$group" == "") && [info exists data(group)]} {
00705         set group $data(group)
00706     }
00707     set data(cmnd) "fetch"
00708     return [::nntp::command $name "XINDEX $group"]    
00709 }
00710 
00711 ret  ::nntp::_xmotd (type name , optional since ="") {
00712     upvar 0 ::nntp::${name}data data
00713 
00714     if {"$since" != ""} {
00715         set since [clock seconds]
00716     }
00717     set since [clock format [clock scan $since] -format "%y%m%d %H%M%S"]
00718     set data(cmnd) "fetch"
00719     return [::nntp::command $name "XMOTD $since"]    
00720 }
00721 
00722 ret  ::nntp::_xover (type name , optional list ="" , optional last ="") {
00723     upvar 0 ::nntp::${name}data data
00724     if {![regexp -- {\d+-\d+} $list]} {
00725         if {"$last" != ""} {
00726             set list "$list-$last"
00727         } else {
00728             set list ""
00729     }
00730     }
00731     set data(cmnd) "fetch"
00732     return [::nntp::command $name "XOVER $list"]
00733 }
00734 
00735 ret  ::nntp::_xpat (type name , optional header ="subject" , optional list =1 , optional last ="" , type args) {
00736     upvar 0 ::nntp::${name}data data
00737 
00738     set patterns ""
00739 
00740     if {![regexp -- {\d+-\d+} $list]} {
00741         if {("$last" != "") && ([string is digit $last])} {
00742             set list "$list-$last"
00743         }
00744     } elseif {"$last" != ""} {
00745         set patterns "$last"
00746     }
00747     
00748     if {[llength $args] > 0} {
00749         set patterns "$patterns $args"
00750     }
00751 
00752     if {"$patterns" == ""} {
00753         set patterns "*"
00754     }
00755     
00756     set data(cmnd) "fetch"
00757     return [::nntp::command $name "XPAT $header $list $patterns"]
00758 }
00759 
00760 ret  ::nntp::_xpath (type name , optional msgid ="") {
00761     upvar 0 ::nntp::${name}data data
00762 
00763     set data(cmnd) "msg"
00764     return [::nntp::command $name "XPATH $msgid"]
00765 }
00766 
00767 ret  ::nntp::_xsearch (type name , type args) {
00768     set res [::nntp::command $name "XSEARCH"]
00769     if {!$res} {
00770         return ""
00771     }
00772     return [::nntp::squirt $name "$args"]    
00773 }
00774 
00775 ret  ::nntp::_xthread (type name , type args) {
00776     upvar 0 ::nntp::${name}data data
00777 
00778     if {[llength $args] > 0} {
00779         set filename "dbinit"
00780     } else {
00781         set filename "thread"
00782     }
00783     set data(cmnd) "fetchbinary"
00784     return [::nntp::command $name "XTHREAD $filename"]
00785 }
00786 
00787 /* */
00788 /* */
00789 /*  Helper methods*/
00790 /* */
00791 
00792 ret  ::nntp::cmd (type name , type cmd) {
00793     upvar 0 ::nntp::${name}data data
00794 
00795     set eol "\015\012"
00796     set sock $data(sock)
00797     if {$data(debug)} {
00798         puts stderr "$sock command $cmd"
00799     }
00800     puts $sock "$cmd"
00801     flush $sock
00802     return
00803 }
00804 
00805 ret  ::nntp::command (type name , type args) {
00806     set res [eval [linsert $args 0 ::nntp::cmd $name]]
00807     
00808     return [::nntp::response $name]
00809 }
00810 
00811 ret  ::nntp::msg (type name) {
00812     upvar 0 ::nntp::${name}data data
00813 
00814     set res [::nntp::okprint $name]
00815     if {!$res} {
00816         return ""
00817     }
00818     return $data(mesg)
00819 }
00820 
00821 ret  ::nntp::groupinfo (type name) {
00822     upvar 0 ::nntp::${name}data data
00823 
00824     set data(group) ""
00825 
00826     if {[::nntp::okprint $name] && [regexp -- {(\d+)\s+(\d+)\s+(\d+)\s+([\w\.]+)} \
00827             $data(mesg) match count first last data(group)]} {
00828         return [list $count $first $last $data(group)]
00829     }
00830     return ""
00831 }
00832 
00833 ret  ::nntp::msgid (type name) {
00834     upvar 0 ::nntp::${name}data data
00835 
00836     set result ""
00837     if {[::nntp::okprint $name] && \
00838             [regsub -- {\s+<[^>]+>} $data(mesg) {} result]} {
00839         return $result
00840     } else {
00841         return ""
00842     }
00843 }
00844 
00845 ret  ::nntp::status (type name) {
00846     upvar 0 ::nntp::${name}data data
00847 
00848     set result ""
00849     if {[::nntp::okprint $name] && \
00850             [regexp -- {\d+\s+<[^>]+>} $data(mesg) result]} {
00851         return $result
00852     } else {
00853         return ""
00854     }
00855 }
00856 
00857 ret  ::nntp::fetch (type name) {
00858     upvar 0 ::nntp::${name}data data
00859 
00860     set eol "\012"
00861 
00862     if {![::nntp::okprint $name]} {
00863         return ""
00864     }
00865     set sock $data(sock)
00866 
00867     if {$data(binary)} {
00868     set oldenc [fconfigure $sock -encoding]
00869     fconfigure $sock -encoding binary
00870     }
00871 
00872     set result [list ]
00873     while {![eof $sock]} {
00874         gets $sock line
00875         regsub -- {\015?\012$} $line $data(eol) line
00876 
00877         if {[string match "." $line]} {
00878             break
00879         }
00880     if { [string match "..*" $line] } {
00881         lappend result [string range $line 1 end]
00882     } else {
00883         lappend result $line
00884     }
00885     }
00886 
00887     if {$data(binary)} {
00888     fconfigure $sock -encoding $oldenc
00889     }
00890 
00891     return $result
00892 }
00893 
00894 ret  ::nntp::response (type name) {
00895     upvar 0 ::nntp::${name}data data
00896 
00897     set eol "\012"
00898 
00899     set sock $data(sock)
00900 
00901     gets $sock line
00902     set data(code) 0
00903     set data(mesg) ""
00904 
00905     if {$line == ""} {
00906         error "nntp: unexpected EOF on $sock\n"
00907     }
00908 
00909     regsub -- {\015?\012$} $line "" line
00910 
00911     set result [regexp -- {^((\d\d)(\d))\s*(.*)} $line match \
00912             data(code) val1 val2 data(mesg)]
00913     
00914     if {$result == 0} {
00915         puts stderr "nntp garbled response: $line\n";
00916         return ""
00917     }
00918 
00919     if {$val1 == 20} {
00920         set data(post) [expr {!$val2}]
00921     }
00922 
00923     if {$data(debug)} {
00924         puts stderr "val1 $val1 val2 $val2"
00925         puts stderr "code '$data(code)'"
00926         puts stderr "mesg '$data(mesg)'"
00927         if {[info exists data(post)]} {
00928             puts stderr "post '$data(post)'"
00929         }
00930     } 
00931 
00932     return [::nntp::returnval $name]
00933 }
00934 
00935 ret  ::nntp::returnval (type name) {
00936     upvar 0 ::nntp::${name}data data
00937 
00938     if {([info exists data(cmnd)]) \
00939             && ($data(cmnd) != "")} {
00940         set command $data(cmnd)
00941     } else {
00942         set command okprint
00943     }
00944     
00945     if {$data(debug)} {
00946         puts stderr "returnval command '$command'"
00947     }
00948 
00949     set data(cmnd) ""
00950     return [::nntp::$command $name]
00951 }
00952 
00953 ret  ::nntp::squirt (type name , optional body ="") {
00954     upvar 0 ::nntp::${name}data data
00955 
00956     set body [split $body \n]
00957 
00958     if {$data(debug)} {
00959         puts stderr "$data(sock) sending [llength $body] lines\n";
00960     }
00961 
00962     foreach line $body {
00963         # Print each line, possibly prepending a dot for lines
00964         # starting with a dot and trimming any trailing \n.
00965     if { [string match ".*" $line] } {
00966         set line ".$line"
00967     }
00968         puts $data(sock) $line
00969     }
00970     puts $data(sock) "."
00971     flush $data(sock)
00972 
00973     if {$data(debug)} {
00974         puts stderr "$data(sock) is finished sending"
00975     }
00976     return [::nntp::response $name]
00977 }
00978 /* eof*/
00979 
00980 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1