]> git.ipfire.org Git - ipfire-2.x.git/blob - src/pakfire/lib/functions.pl
258cf01347325cec540dc8905b59f353fe3632bc
[ipfire-2.x.git] / src / pakfire / lib / functions.pl
1 #!/usr/bin/perl -w
2
3 require "/opt/pakfire/etc/pakfire.conf";
4 require "/var/ipfire/general-functions.pl";
5
6 use File::Basename;
7 use File::Copy;
8 use LWP::UserAgent;
9 use HTTP::Response;
10 use Net::Ping;
11
12 package Pakfire;
13
14 my %pakfiresettings = ();
15 &General::readhash("${General::swroot}/pakfire/settings", \%pakfiresettings);
16
17 sub message {
18 my $message = shift;
19 print "$message\n";
20 logger("$message");
21 }
22
23 sub logger {
24 my $log = shift;
25 system("logger -t pakfire \"$log\"");
26 }
27
28 sub usage {
29 &Pakfire::message("Usage: pakfire <install|remove> <pak(s)>");
30 &Pakfire::message(" <update> - Contacts the servers for new lists of paks.");
31 &Pakfire::message(" <upgrade> - Installs the latest version of all paks.");
32 &Pakfire::message(" <list> - Outputs a short list with all available paks.");
33 &Pakfire::message("");
34 exit 1;
35 }
36
37 sub pinghost {
38 my $host = shift;
39
40 $p = Net::Ping->new();
41 if ($p->ping($host)) {
42 logger("$host is alive.");
43 return 1;
44 } else {
45 logger("$host is dead.");
46 return 0;
47 }
48 $p->close();
49 }
50
51 sub fetchfile {
52 my $getfile = shift;
53 my $gethost = shift;
54 my (@server, $host, $proto, $file, $allok, $i);
55
56 use File::Basename;
57 $bfile = basename("$getfile");
58
59 $i = 0;
60 while (($allok == 0) && $i < 5) {
61 $i++;
62
63 if ("$gethost" eq "") {
64 @server = selectmirror();
65 $proto = $server[0];
66 $host = $server[1];
67 $file = "$server[2]/$getfile";
68 } else {
69 $host = $gethost;
70 $file = $getfile;
71 }
72
73 $proto = "HTTP" unless $proto;
74
75 logger("Trying to get $file from $host ($proto).");
76
77 my $ua = LWP::UserAgent->new;
78 $ua->agent("Pakfire/$Conf::version");
79 $ua->timeout(5);
80
81 my %proxysettings=();
82 &General::readhash("${General::swroot}/proxy/advanced/settings", \%proxysettings);
83
84 if ($proxysettings{'UPSTREAM_PROXY'}) {
85 logger("Using upstream proxy: \"$proxysettings{'UPSTREAM_PROXY'}\"");
86 if ($proxysettings{'UPSTREAM_USER'}) {
87 $ua->proxy("http","http://$proxysettings{'UPSTREAM_USER'}:$proxysettings{'UPSTREAM_PASSWORD'}@"."$proxysettings{'UPSTREAM_PROXY'}/");
88 logger(" Logging in with: \"$proxysettings{'UPSTREAM_USER'}\" - \"$proxysettings{'UPSTREAM_PASSWORD'}\"");
89 } else {
90 $ua->proxy("http","http://$proxysettings{'UPSTREAM_PROXY'}/");
91 }
92 }
93
94 my $response = $ua->get("http://$host/$file");
95
96 my $code = $response->code();
97 my $log = $response->status_line;
98 logger("HTTP-Status-Code: $code - $log");
99
100 if ( $code eq "500" ) {
101 message("Giving up: There was no chance to get teh file \"$getfile\" from any available server.\nThere was an error on the way. Please fix it.");
102 return 1;
103 }
104
105 if ($response->is_success) {
106 if (open(FILE, ">$Conf::tmpdir/$bfile")) {
107 print FILE $response->content;
108 close(FILE);
109 logger("File received. Start checking signature...");
110 if (system("gpg --verify \"$Conf::tmpdir/$bfile\" &>/dev/null") eq 0) {
111 logger("Signature of $bfile is fine.");
112 move("$Conf::tmpdir/$bfile","$Conf::cachedir/$bfile");
113 } else {
114 message("The downloaded file ($file) wasn't verified by IPFire.org. Sorry - Exiting...");
115 exit 1;
116 }
117 logger("Download successfully done from $host (file: $file).");
118 $allok = 1;
119 return 0;
120 } else {
121 logger("Could not open $Conf::cachedir/$bfile for writing.");
122 }
123 } else {
124 logger("Download $file failed from $host ($proto): $log");
125 }
126 }
127 message("Giving up: There was no chance to get the file \"$getfile\" from any available server.\nMay be you should run \"pakfire update\" to get some new servers.");
128 return 1;
129 }
130
131 sub getmirrors {
132 use File::Copy;
133
134 logger("Try to get a mirror list.");
135
136 fetchfile("lists/$Conf::version-server-list", "$Conf::mainserver");
137 move("$Conf::cachedir/$Conf::version-server-list", "$Conf::dbdir/lists/$Conf::version-server-list");
138 }
139
140 sub selectmirror {
141 ### Check if there is a current server list and read it.
142 # If there is no list try to get one.
143 my $count = 0;
144 while (!(open(FILE, "<$Conf::dbdir/lists/$Conf::version-server-list")) && ($count lt 5)) {
145 $count++;
146 getmirrors();
147 }
148 if ($count == 5) {
149 message("Could not find or download a server list.");
150 exit 1;
151 }
152 my @lines = <FILE>;
153 close(FILE);
154
155 ### Count the number of the servers in the list
156 my $scount = 0;
157 foreach (@lines) {
158 $scount++;
159 }
160 logger("$scount servers found in list.");
161
162 ### Choose a random server and test if it is online
163 # If the check fails try a new server.
164 # This will never give up.
165 my $found = 0;
166 my $servers = 0;
167 while ($found == 0) {
168 $server = int(rand($scount) + 1);
169 $servers = 0;
170 my ($line, $proto, $path, $host);
171 my @templine;
172 foreach $line (@lines) {
173 $servers++;
174 if ($servers eq $server) {
175 @templine = split(/\;/, $line);
176 $proto = $templine[0];
177 $host = $templine[1];
178 $path = $templine[2];
179 if (pinghost("$host")) {
180 $found = 1;
181 return ($proto, $host, $path);
182 }
183 }
184 }
185 }
186 }
187
188 sub dbgetlist {
189 ### Update the database if the file is older than one day.
190 # If you pass &Pakfire::dbgetlist(force) the list will be downloaded.
191 # Usage is always with an argument.
192 my $force = shift;
193 my $age;
194
195 use File::Copy;
196
197 if ( -e "$Conf::dbdir/lists/packages_list.db" ) {
198 my @stat = stat("$Conf::dbdir/lists/packages_list.db");
199 my $time = time();
200 $age = $time - $stat[9];
201 } else {
202 # Force an update.
203 $age = "86401";
204 }
205
206 if (("$age" gt 86400) || ("$force" eq "force")) {
207 #cleanup();
208 fetchfile("lists/packages_list.db", "");
209 move("$Conf::cachedir/packages_list.db", "$Conf::dbdir/lists/packages_list.db");
210 }
211 }
212
213 sub dblist {
214 ### This subroutine lists the packages.
215 # You may also pass a filter: &Pakfire::dblist(filter)
216 # Usage is always with two arguments.
217 # filter may be: all, notinstalled, installed
218 my $filter = shift;
219 my $forweb = shift;
220 my @meta;
221 my @updatepaks;
222 my $file;
223 my $line;
224 my $prog;
225 my ($name, $version, $release);
226 my @templine;
227
228 ### Make sure that the list is not outdated.
229 dbgetlist("noforce");
230
231 open(FILE, "<$Conf::dbdir/lists/packages_list.db");
232 my @db = <FILE>;
233 close(FILE);
234
235 if ("$filter" eq "upgrade") {
236 opendir(DIR,"$Conf::dbdir/meta");
237 my @files = readdir(DIR);
238 closedir(DIR);
239 foreach $file (@files) {
240 next if ( $file eq "." );
241 next if ( $file eq ".." );
242 open(FILE, "<$Conf::dbdir/meta/$file");
243 @meta = <FILE>;
244 close(FILE);
245 foreach $line (@meta) {
246 @templine = split(/\: /,$line);
247 if ("$templine[0]" eq "Name") {
248 $name = $templine[1];
249 chomp($name);
250 } elsif ("$templine[0]" eq "ProgVersion") {
251 $version = $templine[1];
252 chomp($version);
253 } elsif ("$templine[0]" eq "Release") {
254 $release = $templine[1];
255 chomp($release);
256 }
257 }
258 foreach $prog (@db) {
259 @templine = split(/\;/,$prog);
260 if (("$name" eq "$templine[0]") && ("$release" < "$templine[2]" )) {
261 push(@updatepaks,$name);
262 if ("$forweb" eq "forweb") {
263 print "<option value=\"$name\">Update: $name -- Version: $version -> $templine[1] -- Release: $release -> $templine[2]</option>\n";
264 } else {
265 print "Update: $name\nVersion: $version -> $templine[1]\nRelease: $release -> $templine[2]\n\n";
266 }
267 }
268 }
269 }
270 return @updatepaks;
271 } else {
272 my $line;
273 my @templine;
274 foreach $line (sort @db) {
275 @templine = split(/\;/,$line);
276 if ("$filter" eq "notinstalled") {
277 next if ( -e "$Conf::dbdir/installed/meta-$templine[0]" );
278 } elsif ("$filter" eq "installed") {
279 next unless ( -e "$Conf::dbdir/installed/meta-$templine[0]" );
280 }
281 if ("$forweb" eq "forweb") {
282 print "<option value=\"$templine[0]\">$templine[0]-$templine[1]-$templine[2]</option>\n";
283 } else {
284 print "Name: $templine[0]\nProgVersion: $templine[1]\nRelease: $templine[2]\n\n";
285 }
286 }
287 }
288 }
289
290 sub resolvedeps {
291 my $pak = shift;
292
293 getmetafile("$pak");
294
295 message("");
296 message("## Resolving dependencies for $pak...");
297 #if (&isinstalled($pak) eq 0) {
298 # my @empty;
299 # return @empty;
300 #}
301
302 open(FILE, "<$Conf::dbdir/meta/meta-$pak");
303 my @file = <FILE>;
304 close(FILE);
305
306 my $line;
307 my (@templine, @deps, @tempdeps, @all);
308 foreach $line (@file) {
309 @templine = split(/\: /,$line);
310 if ("$templine[0]" eq "Dependencies") {
311 @deps = split(/ /, $templine[1]);
312 }
313 }
314 chomp (@deps);
315 foreach (@deps) {
316 if ($_) {
317 my $return = &isinstalled($_);
318 if ($return eq 0) {
319 message("### Dependency is already installed: $_");
320 } else {
321 message("### Need to install dependency: $_");
322 push(@tempdeps,$_);
323 push(@all,$_);
324 }
325 }
326 }
327
328 #my @tempdeps = @deps;
329 foreach (@tempdeps) {
330 if ($_) {
331 my @newdeps = resolvedeps("$_");
332 foreach(@newdeps) {
333 unless (($_ eq " ") || ($_ eq "")) {
334 my $return = &isinstalled($_);
335 if ($return eq 0) {
336 message("### Dependency is already installed: $_");
337 } else {
338 message("### Need to install dependency: $_");
339 push(@all,$_);
340 }
341 }
342 }
343 }
344 }
345 chomp (@all);
346 return @all;
347 }
348
349 sub cleanup {
350 my $dir = shift;
351 my $path;
352
353 if ( "$dir" eq "meta" ) {
354 $path = "$Conf::dbdir/meta";
355 } elsif ( "$dir" eq "tmp" ) {
356 $path = "$Conf::tmpdir";
357 }
358 chdir("$path");
359 opendir(DIR,".");
360 my @files = readdir(DIR);
361 closedir(DIR);
362 foreach (@files) {
363 unless (($_ eq ".") || ($_ eq "..")) {
364 system("rm -rf $_");
365 }
366 }
367 }
368
369 sub getmetafile {
370 my $pak = shift;
371
372 logger("Going to download meta-$pak.");
373
374 unless ( -e "$Conf::dbdir/meta/meta-$pak") {
375 fetchfile("meta/meta-$pak", "");
376 move("$Conf::cachedir/meta-$pak", "$Conf::dbdir/meta/meta-$pak");
377 }
378
379 open(FILE, "<$Conf::dbdir/meta/meta-$pak");
380 my @line = <FILE>;
381 close(FILE);
382
383 open(FILE, ">$Conf::dbdir/meta/meta-$pak");
384 foreach (@line) {
385 my $string = $_;
386 $string =~ s/\r\n/\n/g;
387 print FILE $string;
388 }
389 close(FILE);
390 return 1;
391 }
392
393 sub getsize {
394 my $pak = shift;
395
396 getmetafile("$pak");
397
398 open(FILE, "<$Conf::dbdir/meta/meta-$pak");
399 my @file = <FILE>;
400 close(FILE);
401
402 my $line;
403 my @templine;
404 foreach $line (@file) {
405 @templine = split(/\: /,$line);
406 if ("$templine[0]" eq "Size") {
407 chomp($templine[1]);
408 return $templine[1];
409 }
410 }
411 return 0;
412 }
413
414 sub decryptpak {
415 my $pak = shift;
416
417 cleanup("tmp");
418
419 my $file = getpak("$pak", "noforce");
420
421 my $return = system("cd $Conf::tmpdir/ && gpg -d < $Conf::cachedir/$file | tar x >/dev/null 2>&1");
422 $return %= 255;
423 logger("Decryption process returned the following: $return");
424 if ($return != 0) { exit 1; }
425 }
426
427 sub getpak {
428 my $pak = shift;
429 my $force = shift;
430
431 getmetafile("$pak");
432
433 open(FILE, "<$Conf::dbdir/meta/meta-$pak");
434 my @file = <FILE>;
435 close(FILE);
436
437 my $line;
438 my $file;
439 my @templine;
440 foreach $line (@file) {
441 @templine = split(/\: /,$line);
442 if ("$templine[0]" eq "File") {
443 chomp($templine[1]);
444 $file = $templine[1];
445 }
446 }
447
448 unless ($file) {
449 message("No filename given in meta-file. Please phone the developers.");
450 exit 1;
451 }
452
453 unless ( "$force" eq "force" ) {
454 if ( -e "$Conf::cachedir/$file" ) {
455 return $file;
456 }
457 }
458
459 fetchfile("paks/$file", "");
460 return $file;
461 }
462
463 sub setuppak {
464 my $pak = shift;
465
466 message("################################################################################");
467 message("# --> Installing: $pak");
468 message("################################################################################");
469
470 decryptpak("$pak");
471
472 my $return = system("cd $Conf::tmpdir && NAME=$pak ./install.sh >> $Conf::logdir/install-$pak.log 2>&1");
473 $return %= 255;
474 if ($return == 0) {
475 move("$Conf::tmpdir/ROOTFILES", "$Conf::dbdir/rootfiles/$pak");
476 cleanup("tmp");
477 copy("$Conf::dbdir/meta/meta-$pak","$Conf::dbdir/installed/");
478 message("Setup completed. Congratulations!");
479 message("################################################################################");
480 } else {
481 message("Setup returned: $return. Sorry. Please search our forum to find a solution for this problem.");
482 exit $return;
483 }
484 return $return;
485 }
486
487 sub isinstalled {
488 my $pak = shift;
489 if ( open(FILE,"<$Conf::dbdir/installed/meta-$pak") ) {
490 close(FILE);
491 return 0;
492 } else {
493 return 1;
494 }
495 }
496
497 sub upgradepak {
498 my $pak = shift;
499
500 message("We are going to upgrade: $pak");
501
502 decryptpak("$pak");
503
504 my $return = system("cd $Conf::tmpdir && NAME=$pak ./update.sh >> $Conf::logdir/update-$pak.log 2>&1");
505 $return %= 255;
506 if ($return == 0) {
507 move("$Conf::tmpdir/ROOTFILES", "$Conf::dbdir/rootfiles/$pak");
508 cleanup("tmp");
509 copy("$Conf::dbdir/meta/meta-$pak","$Conf::dbdir/installed/");
510 message("Upgrade completed. Congratulations!");
511 } else {
512 message("Setup returned: $return. Sorry. Please search our forum to find a solution for this problem.");
513 exit $return;
514 }
515 return $return;
516 }
517
518 sub removepak {
519 my $pak = shift;
520
521 message("We are going to uninstall: $pak");
522
523 decryptpak("$pak");
524
525 my $return = system("cd $Conf::tmpdir && NAME=$pak ./uninstall.sh >> $Conf::logdir/uninstall-$pak.log 2>&1");
526 $return %= 255;
527 if ($return == 0) {
528 open(FILE, "<$Conf::dbdir/rootfiles/$pak");
529 my @file = <FILE>;
530 close(FILE);
531 foreach (@file) {
532 my $line = $_;
533 chomp($line);
534 system("echo \"Removing: $line\" >> $Conf::logdir/uninstall-$pak.log 2>&1");
535 system("cd / && rm -rf $line >> $Conf::logdir/uninstall-$pak.log 2>&1");
536 }
537 unlink("$Conf::dbdir/rootfiles/$pak");
538 cleanup("tmp");
539 message("Uninstall completed. Congratulations!");
540 } else {
541 message("Setup returned: $return. Sorry. Please search our forum to find a solution for this problem.");
542 exit $return;
543 }
544 return $return;
545 }
546
547 sub beautifysize {
548 my $size = shift;
549 $size = $size / 1024;
550 my $unit;
551
552 if ($size > 1023) {
553 $size = ($size / 1024);
554 $unit = "MB";
555 } else {
556 $unit = "KB";
557 }
558 $size = sprintf("%.2f" , $size);
559 my $string = "$size $unit";
560 return $string;
561 }
562
563 sub makeuuid {
564 unless ( -e "$Conf::dbdir/uuid" ) {
565 open(FILE, "</proc/sys/kernel/random/uuid");
566 my @line = <FILE>;
567 close(FILE);
568
569 open(FILE, ">$Conf::dbdir/uuid");
570 foreach (@line) {
571 print FILE $_;
572 }
573 close(FILE);
574 }
575 }
576
577 sub senduuid {
578 if ($pakfiresettings{'UUID'} ne "off") {
579 unless("$Conf::uuid") {
580 $Conf::uuid = `cat $Conf::dbdir/uuid`;
581 }
582 logger("Sending my uuid: $Conf::uuid");
583 fetchfile("cgi-bin/counter?ver=$Conf::version&uuid=$Conf::uuid", "$Conf::mainserver");
584 system("rm -f $Conf::cachedir/counter* 2>/dev/null");
585 }
586 }
587
588 sub checkcryptodb {
589 my $myid = "64D96617"; # Our own gpg-key
590 my $trustid = "65D0FD58"; # Id of CaCert
591 my $ret = system("gpg --list-keys | grep -q $myid");
592 unless ( "$ret" eq "0" ) {
593 message("The GnuPG isn't configured corectly. Trying now to fix this.");
594 system("gpg --keyserver wwwkeys.de.pgp.net --always-trust --recv-key $myid");
595 system("gpg --keyserver wwwkeys.de.pgp.net --always-trust --recv-key $trustid");
596 }
597 }
598
599 1;