~grubng-dev/grubng/clients-python

« back to all changes in this revision

Viewing changes to grub.py

  • Committer: gh0st
  • Date: 2010-07-27 21:59:58 UTC
  • Revision ID: gh0st@vortex-20100727215958-xt4lt1pn0hckxa5u
* Added redirect checks

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
#
3
3
## Python Grub Client
4
4
## http://www.grub.org
5
 
## 
 
5
##
6
6
## Copyright (c) 2008 Giorgos Logiotatidis (seadog@sealabs.net)
7
7
## Copyright (c) 2009-2010 Bohdan Turkynewych <tb0hdan@gmail.com>
8
8
##
10
10
## it under the terms of the GNU General Public License as published by
11
11
## the Free Software Foundation; either version 3 of the License, or
12
12
## (at your option) any later version.
13
 
## 
 
13
##
14
14
## Grub.py is distributed in the hope that it will be useful,
15
15
## but WITHOUT ANY WARRANTY; without even the implied warranty of
16
16
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
17
## GNU General Public License for more details.
18
 
## 
 
18
##
19
19
## You should have received a copy of the GNU General Public License
20
20
## along with Grub.py; if not, write to the Free Software
21
21
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
78
78
    def __init__(self, name="Uploader"):
79
79
        Thread.__init__(self)
80
80
        self.setName(name)
81
 
        self._sleeptime = 10 
 
81
        self._sleeptime = 10
82
82
        self.LOOP = True
83
 
    
 
83
 
84
84
    def sleep(self, error, seconds):
85
85
        printer("%s Retrying in %s seconds" % (error, seconds), error=1)
86
86
        time.sleep(seconds)
87
 
    
 
87
 
88
88
    def run(self):
89
 
        while self.LOOP:           
 
89
        while self.LOOP:
90
90
            if os.path.exists( DATAPRFX + WORKUNITS ) == False:
91
91
                # nothing ready yet. Get some sleep and try again later
92
92
                time.sleep(self._sleeptime)
93
93
                continue
94
 
            
 
94
 
95
95
            threadlock.acquire()
96
 
            
 
96
 
97
97
            try:
98
 
                fp = open(DATAPRFX + WORKUNITS, "rb")           
 
98
                fp = open(DATAPRFX + WORKUNITS, "rb")
99
99
                workUnit = fp.readlines()
100
100
                fp.close()
101
101
                fp = open(DATAPRFX + WORKUNITS, "wb")
103
103
                fp.close()
104
104
            except IOError, e:
105
105
                printer( str(DATAPRFX + WORKUNITS) + "open failed in %s: %s" % (self.getName(), e), debug=1)
106
 
            
 
106
 
107
107
            threadlock.release()
108
 
               
 
108
 
109
109
            # keep only the first line of the file
110
110
            try:
111
111
                workUnit = workUnit[0].strip()
114
114
                # sleep some time and then retry
115
115
                time.sleep(self._sleeptime)
116
116
                continue
117
 
            
 
117
 
118
118
            if len(workUnit) > 0:
119
119
                try:
120
120
                    host, url, filename = workUnit.split()
121
121
                except ValueError:
122
122
                    # oops maybe wrong entry. lets ignore
123
123
                    continue
124
 
                    
 
124
 
125
125
                # compress the workunit
126
126
                try:
127
127
                    self.compressWorkUnit(DATAPRFX + filename)
129
129
                    # hmm arc file maybe deleted. just move on
130
130
                    printer("CompressWorkUnit error %s:%s" % (self.getName(), e), debug=1)
131
131
                    continue
132
 
             
 
132
 
133
133
                i=0
134
134
                while self.LOOP:
135
135
                    i+=1
136
 
                    
 
136
 
137
137
                    if i==10:
138
138
                        break
139
 
                    
 
139
 
140
140
                    try:
141
141
                        result = self.uploadWorkUnit(host, url, DATAPRFX + filename)
142
142
                    except (httplib.HTTPException, socket.error):
154
154
                            copy(DATAPRFX + filename, "%s.401" % str(DATAPRFX + filename))
155
155
                            copy("%s.wu" % filename, "%s.wu.401" % str(DATAPRFX + filename ))
156
156
                        break
157
 
                       
158
 
                self.cleanup(DATAPRFX + filename)  
 
157
 
 
158
                self.cleanup(DATAPRFX + filename)
159
159
 
160
160
    def compressWorkUnit(self, filename):
161
161
        # gzip the data
162
162
        printer("Thread %s is packing" % self.getName())
163
163
        farc = open(filename, 'rb')
164
164
        f = gzip.open("%s.gz" % filename, 'wb')
165
 
        
 
165
 
166
166
        data = farc.read(500)
167
167
        while len(data) > 0:
168
168
            f.write(data)
169
169
            data = farc.read(500)
170
 
        
 
170
 
171
171
        f.close()
172
172
        farc.close()
173
173
        printer("Thread %s finished packing" % self.getName(), debug=1)
174
 
        
 
174
 
175
175
    def compressWorkUnits(self, filelist):
176
176
        # A (very) simple procedure
177
177
        # that compresses multiple files
181
181
                os.remove(workunit)
182
182
            except:
183
183
                pass
184
 
        
185
 
            
186
 
    def cleanup(self, filename): 
 
184
 
 
185
 
 
186
    def cleanup(self, filename):
187
187
        try:
188
188
            # Call it poor programming practices,
189
189
            # but it has to check file *BEFORE*
198
198
                    os.remove("%s.wu" % filename)
199
199
        except (OSError, TypeError), e:
200
200
            printer("Cleanup error %s:%s" % (self.getName(), e), debug=1)
201
 
            
 
201
 
202
202
 
203
203
    def join(self):
204
204
        self.LOOP=False
205
 
         
 
205
 
206
206
 
207
207
    def uploadWorkUnit(self, host, url, filename):
208
208
        # Preserve original filename
211
211
        filename = "%s.gz" % filename 
212
212
        start_stamp = int(time.time())
213
213
        printer("Thread %s is uploading %s" % (self.getName(), filename))
214
 
        
 
214
 
215
215
        try:
216
216
            fsize = os.path.getsize(filename)
217
217
            f = open("%s" % filename, "rb")
219
219
            # the file does not exist, return a 401 to ignore that
220
220
            printer("uploadWorkUnit error %s: %s" % (self.getName(), e), debug=1)
221
221
            return -401
222
 
        
 
222
 
223
223
        try:
224
224
            conn = httplib.HTTP(host)
225
225
            conn.putrequest('PUT', url)
321
321
                try:
322
322
                    line = line.split()[1].split('/')
323
323
                    self.filename = line[len(line)-1][:-3]
324
 
                    
 
324
 
325
325
                    if os.path.exists("%s.gz" % str(DATAPRFX + self.filename)):
326
326
                        # oops file already exists!
327
327
                        # The server feed us previous stuff
333
333
                    return False
334
334
        return False
335
335
 
 
336
    def check_redir(self, c_host,c_url, c_headers):
 
337
        isRedirect = False
 
338
        # Check for redirect, if failed - return unchanged values
 
339
        try:
 
340
            c_conn = httplib.HTTPConnection(c_host)
 
341
            c_conn.request("GET", c_url, headers=c_headers)
 
342
            c_response = c_conn.getresponse()
 
343
            c_conn.close()
 
344
        except:
 
345
            return isRedirect, c_host, c_url
 
346
        
 
347
        # HTTP/1.0 302 Moved Temporarily
 
348
        # TODO: Add Moved Permanently
 
349
        if c_response.status == 302:
 
350
            for c_header in c_response.getheaders():
 
351
                if c_header[0] == 'location':
 
352
                    newloc = re_sub('^.+\://', '', c_header[1])
 
353
                    isRedirect = True
 
354
                    break
 
355
        else:
 
356
            return isRedirect, c_host, c_url
 
357
        # Split one single location into host and url
 
358
        newhost=newloc.split('/')[0]
 
359
        newurl=re_sub('^'+newhost, '', newloc)
 
360
        return isRedirect, newhost,newurl
 
361
 
 
362
 
336
363
 
337
364
    def run(self):
338
365
        while self.LOOP:
339
366
            if self.fetchWorkUnit() == False or self.checkWorkUnit() == False:
340
367
                # oops, bad workunit, let's move to a new one
341
368
                continue
342
 
            
 
369
 
343
370
            # if debug save workunit
344
371
            if DEBUG:
345
372
                f = open("%s.wu" % str(DATAPRFX + self.filename), 'wb')
386
413
                    # lets hope it's just a wrong entry and continue
387
414
                    continue
388
415
 
389
 
 
390
416
                #python2.4 compatibility with try/finally
391
417
                try:
392
418
                    try:
393
 
   
 
419
 
394
420
                        headers = {
395
421
#                            'Accept':'*/*',
396
422
#                            'Accept-encoding':'gzip,deflate',
397
423
                            'Connection':'close',
398
 
                            }                        
 
424
                            }
399
425
 
400
426
                        while True:
401
427
                            header = wuIterator.next().strip()
402
428
                            if header == '':
403
429
                                break
404
 
                            
 
430
 
405
431
                            headerType, headerValue = header.split(':', 1)
406
432
                            headerType = headerType.strip()
407
433
                            headerValue = headerValue.strip()
408
 
                            
 
434
 
409
435
                            headers[headerType] = headerValue
410
436
 
411
437
                        ip = socket.gethostbyname(host)
 
438
                        # Follow redirects
 
439
                        # Preserve original host and url
 
440
                        host_orig = host
 
441
                        url_orig  = url
 
442
                        redir,host,url = self.check_redir(host,url,headers)
 
443
 
412
444
                        conn = httplib.HTTPConnection(host)
413
445
 
414
446
                        conn.request("GET", url, headers=headers)
415
447
 
 
448
                        if redir:
 
449
                            printer("Thread %s has found redirect" % self.getName())
 
450
                            if DEBUG:
 
451
                                printer("Thread %s has found redirectOld url %s, new url %s" % (self.getName(), host_orig + url_orig, host + url))
 
452
 
 
453
 
416
454
                        if SHOWURL == True:
417
455
                            printer ("GET http://%s%s" % (host, url))
418
456
 
419
457
                        response = conn.getresponse()
420
 
                     
 
458
 
421
459
                        if response.version == "10":
422
460
                            data += "HTTP/1.0 "
423
461
                        else:
436
474
 
437
475
                        # check for compressed content
438
476
                        if response.getheader('content-encoding') in ('gzip', 'deflate'):
439
 
                            body = self.decompressData(body, response.getheader('content-encoding'))                   
 
477
                            body = self.decompressData(body, response.getheader('content-encoding'))
440
478
 
441
479
                        responseType = self.getResponseType(response.msg.type)
442
 
            
 
480
 
443
481
                        # close connection
444
482
                        conn.close()
445
483
 
449
487
                        ip = "0.0.0.0"
450
488
                        responseType = 'application/x-grub-error'
451
489
                        #printer("Invalid URL %s%s %s" % (host,url,e), error=1)
452
 
                    
 
490
 
453
491
 
454
492
                finally:
455
493
                    self.arc.writelines("http://%s%s %s %s %s %s\n" % (host, url, ip, time.strftime("%Y%m%d%H%M%S", time.localtime()), responseType, len(data) + len(body)))
482
520
            if len(tmp) == 0:
483
521
                break
484
522
            data += tmp
485
 
            
 
523
 
486
524
        return data
487
 
            
 
525
 
488
526
 
489
527
    def getResponseType(self, responseType):
490
528
        try:
501
539
        try:
502
540
            if method == 'gzip':
503
541
                compressed_data = StringIO(compressed_data)
504
 
                vfile = gzip.GzipFile(fileobj=compressed_data) 
 
542
                vfile = gzip.GzipFile(fileobj=compressed_data)
505
543
                data = vfile.read()
506
544
                vfile.close()
507
545
                compressed_data.close()
516
554
        except Exception, e:
517
555
            printer("Decompress %s: %s" % (self.getName(), e), debug=1)
518
556
            vfile.close()
519
 
            compressed_data.close()  
 
557
            compressed_data.close()
520
558
            raise ValueError
521
559
 
522
560
        return data
523
561
 
524
 
        
 
562
 
525
563
    def writeDataFile(self, host, url):
526
564
        if (len(host) == 0 or len(url) == 0):
527
565
            return
528
 
            
 
566
 
529
567
        threadlock.acquire()
530
 
        try:            
 
568
        try:
531
569
            fp = open(DATAPRFX + WORKUNITS, "ab")
532
570
            fp.write("%s %s %s\n" % (host, url, self.filename))
533
571
            fp.close()
534
572
        except IOError, e:
535
573
            printer("WriteDataFile error %s: %s" % (self.getName(), e), debug=1)
536
574
        threadlock.release()
537
 
        
538
 
            
539
 
    def cleanup(self, real=False):     
 
575
 
 
576
 
 
577
    def cleanup(self, real=False):
540
578
        if real:
541
579
            try:
542
580
                os.remove(self.filename)
543
581
            except (OSError, TypeError), e:
544
582
                printer("Cleanup error %s:%s" % (self.getName(), e), debug=1)
545
 
       
546
 
            
 
583
 
 
584
 
547
585
        self.filename = None
548
586
        self.wu = None
549
587
 
560
598
        logging.error(msg)
561
599
    elif (VERBOSE == True):
562
600
        logging.info(msg)
563
 
            
 
601
 
564
602
def setupLogging():
565
603
    global LOG
566
604
    logging.getLogger('').setLevel(level=logging.DEBUG)
579
617
        handler.setFormatter(formatter)
580
618
        handler.setLevel(logging.DEBUG)
581
619
        logging.getLogger('').addHandler(handler)
582
 
    
583
 
                        
584
620
 
585
621
 
586
622
def printUsage():
587
623
    global VERSION
588
 
    print "Grub Python Client version %s" % VERSION 
 
624
    print "Grub Python Client version %s" % VERSION
589
625
    print "http://www.grub.org/\n"
590
626
    print "Usage:" \
591
627
         "\tgrub.py [-v] [-t NUMBER] -u USERNAME -p PASSWORD\n" \
611
647
            passwdfile = os.path.expanduser("~") + "/.grub/account"
612
648
        elif os.path.exists("./account"):
613
649
            passwdfile = "./account"
614
 
        else:           
 
650
        else:
615
651
            # no username/password specified!
616
652
            printer("No username and/or password specified!", error=1)
617
653
            sys.exit()
619
655
        try:
620
656
            USERNAME, PASSWORD = open(passwdfile).readline().split(':')
621
657
            USERNAME = USERNAME.strip()
622
 
            PASSWORD = PASSWORD.strip()            
 
658
            PASSWORD = PASSWORD.strip()
623
659
        except:
624
660
            printer("Error reading username and password from %s" % passwdfile)
625
661
            sys.exit(1)
640
676
 
641
677
 
642
678
def EnumArcs(extension=None):
643
 
    global DATAPRFX    
 
679
    global DATAPRFX
644
680
    arclist=[]
645
681
    basepath=DATAPRFX
646
682
    for root, dirs, files in os.walk(basepath):
701
737
            printInfo()
702
738
            sys.exit(0)
703
739
        elif o in ("--debug"):
704
 
            DEBUG = True      
 
740
            DEBUG = True
705
741
        elif o in ("--log"):
706
 
            LOG = True      
 
742
            LOG = True
707
743
        elif o in ("--url"):
708
744
            SHOWURL = True
709
745
        elif o in ("--version"):
716
752
    if not DATAPRFX:
717
753
        DATAPRFX = "data/"
718
754
    else:
719
 
        DATAPRFX = addslash(os.path.normpath(DATAPRFX))        
 
755
        DATAPRFX = addslash(os.path.normpath(DATAPRFX))
720
756
 
721
757
    try:
722
758
        if not os.path.exists("data"):
723
 
            os.mkdir("data")            
 
759
            os.mkdir("data")
724
760
    except OSError:
725
761
        printer("Creating data directory failed!", error=1)
726
 
        sys.exit(13)                   
 
762
        sys.exit(13)
727
763
 
728
764
 
729
765
    parseConfig()
734
770
    # Upload packed .ARC's
735
771
    # Most of these can (will) FAIL (Code: 401)
736
772
    Uploader.uploadWorkUnits(Uploader(), EnumArcs('.gz'))
737
 
    # 
738
 
    for i in range(NUM_CLIENTS): 
 
773
    #
 
774
    for i in range(NUM_CLIENTS):
739
775
        w = Crawler(i)
740
776
        workerlist.append(w)
741
 
        w.start()    
 
777
        w.start()
742
778
 
743
779
    for i in range(NUM_UPLOADERS):
744
780
        w = Uploader("Uploader-%s" % i)