- initial import of revision 374 from cnc
[apt.git] / apt-pkg / acquire-method.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: acquire-method.cc,v 1.1 2002/07/23 17:54:50 niemeyer Exp $
4 /* ######################################################################
5
6    Acquire Method
7
8    This is a skeleton class that implements most of the functionality
9    of a method and some useful functions to make method implementation
10    simpler. The methods all derive this and specialize it. The most
11    complex implementation is the http method which needs to provide
12    pipelining, it runs the message engine at the same time it is 
13    downloading files..
14    
15    ##################################################################### */
16                                                                         /*}}}*/
17 // Include Files                                                        /*{{{*/
18 #ifdef __GNUG__
19 #pragma implementation "apt-pkg/acquire-method.h"
20 #endif
21 #include <apt-pkg/acquire-method.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/fileutl.h>
26 #include <apt-pkg/hashes.h>
27
28 #include <iostream>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <unistd.h>
32                                                                         /*}}}*/
33
34 using namespace std;
35
36 // AcqMethod::pkgAcqMethod - Constructor                                /*{{{*/
37 // ---------------------------------------------------------------------
38 /* This constructs the initialization text */
39 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
40         : Flags(Flags) // CNC:2002-07-11
41 {
42    char S[300] = "";
43    char *End = S;
44    strcat(End,"100 Capabilities\n");
45    sprintf(End+strlen(End),"Version: %s\n",Ver);
46
47    if ((Flags & SingleInstance) == SingleInstance)
48       strcat(End,"Single-Instance: true\n");
49    
50    if ((Flags & Pipeline) == Pipeline)
51       strcat(End,"Pipeline: true\n");
52    
53    if ((Flags & SendConfig) == SendConfig)
54       strcat(End,"Send-Config: true\n");
55
56    if ((Flags & LocalOnly) == LocalOnly)
57       strcat(End,"Local-Only: true\n");
58
59    if ((Flags & NeedsCleanup) == NeedsCleanup)
60       strcat(End,"Needs-Cleanup: true\n");
61
62    if ((Flags & Removable) == Removable)
63       strcat(End,"Removable: true\n");
64
65    // CNC:2004-04-27
66    if ((Flags & HasPreferredURI) == HasPreferredURI)
67       strcat(End,"Has-Preferred-URI: true\n");
68    strcat(End,"\n");
69
70    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
71       exit(100);
72
73    SetNonBlock(STDIN_FILENO,true);
74
75    Queue = 0;
76    QueueBack = 0;
77 }
78                                                                         /*}}}*/
79 // AcqMethod::Fail - A fetch has failed                                 /*{{{*/
80 // ---------------------------------------------------------------------
81 /* */
82 void pkgAcqMethod::Fail(bool Transient)
83 {
84    string Err = "Undetermined Error";
85    if (_error->empty() == false)
86       _error->PopMessage(Err);   
87    _error->Discard();
88    Fail(Err,Transient);
89 }
90                                                                         /*}}}*/
91 // AcqMethod::Fail - A fetch has failed                                 /*{{{*/
92 // ---------------------------------------------------------------------
93 /* */
94 void pkgAcqMethod::Fail(string Err,bool Transient)
95 {
96    // Strip out junk from the error messages
97    for (string::iterator I = Err.begin(); I != Err.end(); I++)
98    {
99       if (*I == '\r') 
100          *I = ' ';
101       if (*I == '\n') 
102          *I = ' ';
103    }
104    
105    char S[1024];
106    if (Queue != 0)
107    {
108       snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
109                "Message: %s %s\n",Queue->Uri.c_str(),Err.c_str(),
110                FailExtra.c_str());
111
112       // Dequeue
113       FetchItem *Tmp = Queue;
114       Queue = Queue->Next;
115       delete Tmp;
116       if (Tmp == QueueBack)
117          QueueBack = Queue;
118    }
119    else
120       snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
121                "Message: %s %s\n",Err.c_str(),
122                FailExtra.c_str());
123       
124    // Set the transient flag 
125    if (Transient == true)
126       strcat(S,"Transient-Failure: true\n\n");
127    else
128       strcat(S,"\n");
129    
130    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
131       exit(100);
132 }
133                                                                         /*}}}*/
134 // AcqMethod::URIStart - Indicate a download is starting                /*{{{*/
135 // ---------------------------------------------------------------------
136 /* */
137 void pkgAcqMethod::URIStart(FetchResult &Res)
138 {
139    if (Queue == 0)
140       abort();
141    
142    char S[1024] = "";
143    char *End = S;
144    
145    End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
146    if (Res.Size != 0)
147       End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
148    
149    if (Res.LastModified != 0)
150       End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
151                       TimeRFC1123(Res.LastModified).c_str());
152    
153    if (Res.ResumePoint != 0)
154       End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
155                       Res.ResumePoint);
156       
157    strcat(End,"\n");
158    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
159       exit(100);
160 }
161                                                                         /*}}}*/
162 // AcqMethod::URIDone - A URI is finished                               /*{{{*/
163 // ---------------------------------------------------------------------
164 /* */
165 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
166 {
167    if (Queue == 0)
168       abort();
169    
170    char S[1024] = "";
171    char *End = S;
172    
173    End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
174
175    if (Res.Filename.empty() == false)
176       End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
177    
178    if (Res.Size != 0)
179       End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
180    
181    if (Res.LastModified != 0)
182       End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
183                       TimeRFC1123(Res.LastModified).c_str());
184
185    if (Res.MD5Sum.empty() == false)
186       End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
187    if (Res.SHA1Sum.empty() == false)
188       End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
189
190    // CNC:2002-07-04
191    if (Res.SignatureFP.empty() == false)
192       End += snprintf(End,sizeof(S)-50 - (End - S),"Signature-Fingerprint: %s\n",Res.SignatureFP.c_str());
193
194    if (Res.ResumePoint != 0)
195       End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
196                       Res.ResumePoint);
197
198    if (Res.IMSHit == true)
199       strcat(End,"IMS-Hit: true\n");
200    End = S + strlen(S);
201    
202    if (Alt != 0)
203    {
204       if (Alt->Filename.empty() == false)
205          End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
206       
207       if (Alt->Size != 0)
208          End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
209       
210       if (Alt->LastModified != 0)
211          End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
212                          TimeRFC1123(Alt->LastModified).c_str());
213       
214       if (Alt->MD5Sum.empty() == false)
215          End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
216                          Alt->MD5Sum.c_str());
217       if (Alt->SHA1Sum.empty() == false)
218          End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-SHA1-Hash: %s\n",
219                          Alt->SHA1Sum.c_str());
220       
221       if (Alt->IMSHit == true)
222          strcat(End,"Alt-IMS-Hit: true\n");
223    }
224    
225    strcat(End,"\n");
226    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
227       exit(100);
228
229    // Dequeue
230    FetchItem *Tmp = Queue;
231    Queue = Queue->Next;
232    delete Tmp;
233    if (Tmp == QueueBack)
234       QueueBack = Queue;
235 }
236                                                                         /*}}}*/
237 // AcqMethod::MediaFail - Syncronous request for new media              /*{{{*/
238 // ---------------------------------------------------------------------
239 /* This sends a 403 Media Failure message to the APT and waits for it
240    to be ackd */
241 bool pkgAcqMethod::MediaFail(string Required,string Drive)
242 {
243    char S[1024];
244    snprintf(S,sizeof(S),"403 Media Failure\nMedia: %s\nDrive: %s\n\n",
245             Required.c_str(),Drive.c_str());
246
247    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
248       exit(100);
249    
250    vector<string> MyMessages;
251    
252    /* Here we read messages until we find a 603, each non 603 message is
253       appended to the main message list for later processing */
254    while (1)
255    {
256       if (WaitFd(STDIN_FILENO) == false)
257          return false;
258       
259       if (ReadMessages(STDIN_FILENO,MyMessages) == false)
260          return false;
261
262       string Message = MyMessages.front();
263       MyMessages.erase(MyMessages.begin());
264       
265       // Fetch the message number
266       char *End;
267       int Number = strtol(Message.c_str(),&End,10);
268       if (End == Message.c_str())
269       {  
270          cerr << "Malformed message!" << endl;
271          exit(100);
272       }
273
274       // Change ack
275       if (Number == 603)
276       {
277          while (MyMessages.empty() == false)
278          {
279             Messages.push_back(MyMessages.front());
280             MyMessages.erase(MyMessages.begin());
281          }
282
283          return !StringToBool(LookupTag(Message,"Fail"),false);
284       }
285       
286       Messages.push_back(Message);
287    }   
288 }
289                                                                         /*}}}*/
290 // AcqMethod::Configuration - Handle the configuration message          /*{{{*/
291 // ---------------------------------------------------------------------
292 /* This parses each configuration entry and puts it into the _config 
293    Configuration class. */
294 bool pkgAcqMethod::Configuration(string Message)
295 {
296    ::Configuration &Cnf = *_config;
297    
298    const char *I = Message.c_str();
299    const char *MsgEnd = I + Message.length();
300    
301    unsigned int Length = strlen("Config-Item");
302    for (; I + Length < MsgEnd; I++)
303    {
304       // Not a config item
305       if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
306          continue;
307       
308       I += Length + 1;
309       
310       for (; I < MsgEnd && *I == ' '; I++);
311       const char *Equals = I;
312       for (; Equals < MsgEnd && *Equals != '='; Equals++);
313       const char *End = Equals;
314       for (; End < MsgEnd && *End != '\n'; End++);
315       if (End == Equals)
316          return false;
317       
318       Cnf.Set(DeQuoteString(string(I,Equals-I)),
319               DeQuoteString(string(Equals+1,End-Equals-1)));
320       I = End;
321    }
322    
323    return true;
324 }
325                                                                         /*}}}*/
326 // AcqMethod::Run - Run the message engine                              /*{{{*/
327 // ---------------------------------------------------------------------
328 /* Fetch any messages and execute them. In single mode it returns 1 if
329    there are no more available messages - any other result is a 
330    fatal failure code! */
331 int pkgAcqMethod::Run(bool Single)
332 {
333    while (1)
334    {
335       // Block if the message queue is empty
336       if (Messages.empty() == true)
337       {
338          if (Single == false)
339             if (WaitFd(STDIN_FILENO) == false)
340                break;
341          if (ReadMessages(STDIN_FILENO,Messages) == false)
342             break;
343       }
344             
345       // Single mode exits if the message queue is empty
346       if (Single == true && Messages.empty() == true)
347          return -1;
348       
349       string Message = Messages.front();
350       Messages.erase(Messages.begin());
351       
352       // Fetch the message number
353       char *End;
354       int Number = strtol(Message.c_str(),&End,10);
355       if (End == Message.c_str())
356       {  
357          cerr << "Malformed message!" << endl;
358          return 100;
359       }
360
361       switch (Number)
362       {  
363          case 601:
364          if (Configuration(Message) == false)
365             return 100;
366          break;
367          
368          case 600:
369          {
370             FetchItem *Tmp = new FetchItem;
371             
372             Tmp->Uri = LookupTag(Message,"URI");
373             Tmp->DestFile = LookupTag(Message,"FileName");
374             if (StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
375                Tmp->LastModified = 0;
376             Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
377             Tmp->Next = 0;
378
379             // CNC:2002-07-11
380             if (StringToBool(LookupTag(Message,"Local-Only-IMS"),false) == true
381                 && (Flags & LocalOnly) == 0)
382                Tmp->LastModified = 0;
383             
384             // Append it to the list
385             FetchItem **I = &Queue;
386             for (; *I != 0; I = &(*I)->Next);
387             *I = Tmp;
388             if (QueueBack == 0)
389                QueueBack = Tmp;
390             
391             // Notify that this item is to be fetched.
392             if (Fetch(Tmp) == false)
393                Fail();
394             
395             break;                                           
396          }   
397
398          // CNC:2004-04-27
399          case 679:
400          {
401             char S[1024];
402             snprintf(S,sizeof(S),"179 Preferred URI\nPreferredURI: %s\n\n",
403                      PreferredURI().c_str());
404             if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
405                exit(100);
406             break;
407
408          }
409       }      
410    }
411
412    Exit();
413    return 0;
414 }
415                                                                         /*}}}*/
416 // AcqMethod::Log - Send a log message                                  /*{{{*/
417 // ---------------------------------------------------------------------
418 /* */
419 void pkgAcqMethod::Log(const char *Format,...)
420 {
421    string CurrentURI = "<UNKNOWN>";
422    if (Queue != 0)
423       CurrentURI = Queue->Uri;
424    
425    va_list args;
426    va_start(args,Format);
427
428    // sprintf the description
429    char S[1024];
430    unsigned int Len = snprintf(S,sizeof(S)-4,"101 Log\nURI: %s\n"
431                                "Message: ",CurrentURI.c_str());
432
433    vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
434    strcat(S,"\n\n");
435    
436    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
437       exit(100);
438 }
439                                                                         /*}}}*/
440 // AcqMethod::Status - Send a status message                            /*{{{*/
441 // ---------------------------------------------------------------------
442 /* */
443 void pkgAcqMethod::Status(const char *Format,...)
444 {
445    string CurrentURI = "<UNKNOWN>";
446    if (Queue != 0)
447       CurrentURI = Queue->Uri;
448    
449    va_list args;
450    va_start(args,Format);
451
452    // sprintf the description
453    char S[1024];
454    unsigned int Len = snprintf(S,sizeof(S)-4,"102 Status\nURI: %s\n"
455                                "Message: ",CurrentURI.c_str());
456
457    vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
458    strcat(S,"\n\n");
459    
460    if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
461       exit(100);
462 }
463                                                                         /*}}}*/
464
465 // AcqMethod::FetchResult::FetchResult - Constructor                    /*{{{*/
466 // ---------------------------------------------------------------------
467 /* */
468 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
469                                    IMSHit(false), Size(0), ResumePoint(0)
470 {
471 }
472                                                                         /*}}}*/
473 // AcqMethod::FetchResult::TakeHashes - Load hashes                     /*{{{*/
474 // ---------------------------------------------------------------------
475 /* This hides the number of hashes we are supporting from the caller. 
476    It just deals with the hash class. */
477 void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
478 {
479    MD5Sum = Hash.MD5.Result();
480    SHA1Sum = Hash.SHA1.Result();
481 }
482                                                                         /*}}}*/
483 // vim:sts=3:sw=3