This file is indexed.

/usr/lib/pidgin/lastfm.pl is in pidgin-lastfm 0.4a-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
#!/usr/bin/perl
# Last.fm Info Plugin for Pidgin / libpurple
# Copyright (c) 2008 by Dominik George <pidgin-lastfm@naturalnet.de>
#
# Yippie, my first perl script :-D
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

use Purple;

# Global variables
our $song_joker;
our $ago_joker;
our $time_joker;

# Last.fm API
our $apikey = '4b9aa27d34af5238708afa6807e6a18a';

# Store plugin reference globally
our $plugin;
our $act_timeout;

# Set the current version
our $actversion = "0.4a";

# Information on plugin for libpurple
%PLUGIN_INFO = (
 perl_api_version => 2,
 name => "Last.fm Info Plugin",
 version => $actversion,
 summary => "Display Last.fm info in status message",
 description => "Plugin to display information from your Last.fm profile in Pidgin",
 author => "Dominik George <pidgin-lastfm\@naturalnet.de>",
 url => "http://pidgin-lastfm.naturalnet.de",
 
 # Subs that libpurple has to know
 load => "plugin_load",
 unload => "plugin_unload",
 prefs_info => "prefs_info_cb",
 plugin_action_sub => "plugin_actions_cb"
);

# Called upon plugin initialization
sub plugin_init {
 return %PLUGIN_INFO;
}

# Get everything ready upon loading
sub plugin_load {
 $plugin = shift;
 Purple::Debug::info("lastfm", "Last.fm Plugin loading ...\n");

 # Load or create preferences
 Purple::Prefs::add_none("/plugins/core/lastfm");
 Purple::Prefs::add_string("/plugins/core/lastfm/username", "");
 Purple::Prefs::add_string("/plugins/core/lastfm/content", "user.getRecentTracks");
 Purple::Prefs::add_int("/plugins/core/lastfm/timeout", 30);
 Purple::Prefs::add_bool("/plugins/core/lastfm/nowplayingonly", FALSE);

 # Song info cache from preferences
 Purple::Prefs::add_string("/plugins/core/lastfm/song_joker", "%s");
 Purple::Prefs::add_string("/plugins/core/lastfm/ago_joker", "%a");
 Purple::Prefs::add_string("/plugins/core/lastfm/time_joker", "%t");

 # Setup timeout for refreshing info
 our $act_timeout = Purple::timeout_add($plugin, 1, \&loadinfo_cb, $plugin);

 Purple::Debug::info("lastfm", "Last.fm Plugin loaded.\n");
}

# Cleanly unload plugin
sub plugin_unload {
 my $plugin = shift;

 # Reset status message so we find the standard placeholder next time
 # set_status("%s", "%a", "%t");

 Purple::Debug::info("lastfm", "Last.fm Plugin removed.\n");
}

# Preferences dialog box
sub prefs_info_cb {
 my $frame = Purple::PluginPref::Frame->new();
 
 # Audioscrobbler username
 my $ppref = Purple::PluginPref->new_with_name_and_label("/plugins/core/lastfm/username", "Username:");
 $ppref->set_type(2);
 $ppref->set_max_length(16);
 $frame->add($ppref);

 # Desired content
 $ppref = Purple::PluginPref->new_with_name_and_label("/plugins/core/lastfm/content", "Content:");
 $ppref->set_type(1);
 $ppref->add_choice("Most recent track", "user.getRecentTracks");
 $ppref->add_choice("Top track", "user.getTopTracks");
 $ppref->add_choice("Most recently loved track", "user.getLovedTracks");
 $ppref->add_choice("Most recently banned track", "user.getBannedTracks");
 $frame->add($ppref);

 # Timeout for refreshing content
 $ppref = Purple::PluginPref->new_with_name_and_label("/plugins/core/lastfm/timeout", "Refresh (sec):");
 $ppref->set_type(3);
 $ppref->set_bounds(10, 300);
 $frame->add($ppref);

 # Don't show information when not "now playing"?
 $ppref = Purple::PluginPref->new_with_name_and_label("/plugins/core/lastfm/nowplayingonly", "Show --- when not listening right now");
 $ppref->set_type(1);
 $frame->add($ppref);

 return $frame;
}

# Actions that user can run from UI menu
sub plugin_actions_cb {
 my @actions = ("Refresh info", "Reset placeholders", "Check for updates", "Credits");
}

%plugin_actions = (
 "Refresh info" => \&loadinfo_cb,
 "Reset placeholders" => \&reset_placeholders,
 "Check for updates" => \&checkupdate_cb,
 "Credits" => \&loadcredits_cb
);

# Helper function to escape strings for regex compatibility
sub regex_escape {
 my $string = $_[0];

 $string =~ s/\\/\\\\/g;
 $string =~ s/\//\\\//g;
 $string =~ s/\./\\./g;
 $string =~ s/\+/\\+/g;
 $string =~ s/\?/\\?/g;
 $string =~ s/\^/\\^/g;
 $string =~ s/\$/\\\$/g;
 $string =~ s/\|/\\|/g;
 $string =~ s/\(/\\(/g;
 $string =~ s/\)/\\)/g;
 $string =~ s/\[/\\]/g;
 $string =~ s/\]/\\]/g;
 $string =~ s/\{/\\{/g;
 $string =~ s/\}/\\}/g;

 return $string;
}

# Load placeholders from preferences
sub load_placeholders {
 our $song_joker = Purple::Prefs::get_string("/plugins/core/lastfm/song_joker");
 our $ago_joker = Purple::Prefs::get_string("/plugins/core/lastfm/ago_joker");
 our $time_joker = Purple::Prefs::get_string("/plugins/core/lastfm/time_joker");
}

# Save current placeholders to the preferences
# I hope we will survive SEGFAULTs that way :-)
sub save_placeholders {
 Purple::Prefs::set_string("/plugins/core/lastfm/song_joker", $song_joker);
 Purple::Prefs::set_string("/plugins/core/lastfm/ago_joker", $ago_joker);
 Purple::Prefs::set_string("/plugins/core/lastfm/time_joker", $time_joker);
}

# Reset placeholders to the defaults
sub reset_placeholders {
 load_placeholders();

 our $song_joker = "%s";
 our $ago_joker = "%a";
 our $time_joker = "%t";

 save_placeholders();
}

sub parse_credits_cb {
 my $credits = shift;

 Purple::Notify::message($plugin, 2, "Last.fm Plugin Credits", "The following people helped a lot at developing the plugin:", $credits, NULL, NULL);
}

sub loadcredits_cb {
 my $plugin = shift;
 my $url = "http://pidgin-lastfm.naturalnet.de/res/credits.txt";
 Purple::Util::fetch_url($plugin, $url, TRUE, "Mozilla/5.0", TRUE, \&parse_credits_cb);
}

sub checkupdate_cb {
 my $plugin = shift;

 # URL with text file containing version information
 my $url = "http://pidgin-lastfm.naturalnet.de/res/version.txt";

 # Fetch version information
 Purple::Util::fetch_url($plugin, $url, TRUE, "Mozilla/5.0", TRUE, \&parse_update_cb);
}

sub parse_update_cb {
 my $version = shift;

 my $linebreak = index($version, "\n");
 $version = substr($version, 0, $linebreak);

 Purple::Debug::info("lastfm", "The running version is " . $actversion . ".\n");
 Purple::Debug::info("lastfm", "The most recent version is " . $version . ".\n");

 if ($version > $actversion) {
  Purple::Notify::message($plugin, 2, "Last.fm Plugin", "New version available!", "A new version of the Pidgin Last.FM plugin (version " . $version . ") is available for download.", NULL, NULL);
 } else {
  if ($actversion > $version) {
   Purple::Notify::message($plugin, 2, "Last.fm Plugin", "Your version is from the future!", "You are running a more recent version than the server knows.\n\nI conclude: You are either a developer or a magician :-D ...", NULL, NULL);
  } else {
   Purple::Notify::message($plugin, 2, "Last.fm Plugin", "No new version available!", "You are running the most recent version of the plugin.", NULL, NULL);
  }
 }
}

sub set_status {
 my $song = $_[0];
 my $ago  = $_[1];
 my $time = $_[2];

 # Parse status message and set info
 my $status  = Purple::SavedStatus::get_current();
 my $message = Purple::SavedStatus::get_message($status);
 my $oldmsg  = $message;

 load_placeholders();

 my $song_regex = regex_escape($song_joker);
 my $ago_regex = regex_escape($ago_joker);
 my $time_regex = regex_escape($time_joker);

 Purple::Debug::info("lastfm", "Looking for song joker \"" . $song_joker . "\".\n");

 if ($message =~ /$song_regex/i) {
  Purple::Debug::info("lastfm", "Old message was \"" . $message . "\".\n");
 
  # Now do the regex replacements

  if (($message =~ /$song_regex/i) && ($song ne "") && ($song_joker ne $song)) {
   $message =~ s/$song_regex/$song/i;
   our $song_joker = $song;
  }
 
  if (($message =~ /$ago_regex/i) && ($ago ne "") && ($ago_joker ne $ago)) {
   $message =~ s/$ago_regex/$ago/i;
   our $ago_joker = $ago;
  }
 
  if (($message =~ /$time_regex/i) && ($time ne "") && ($time_joker ne $time)) {
   $message =~ s/$time_regex/$time/i;
   our $time_joker = $time;
  }
 
  save_placeholders();
 
  # Only update if something has changed
  if ($message ne $oldmsg) {
   Purple::Debug::info("lastfm", "Changing to \"" . $message . "\".\n");
   Purple::SavedStatus::set_message($status, $message);
   Purple::SavedStatus::activate($status);
  } else {
   Purple::Debug::info("lastfm", "Status message unchanged.\n");
  }
 } else {
  Purple::Debug::info("lastfm", "Song joker not found ...\n");
  Purple::Debug::info("lastfm", "If you think this is wrong, try resetting the song jokers!\n");
 }

 # Reset timeout so refresh is called regularly 
 my $timeout = Purple::Prefs::get_int("/plugins/core/lastfm/timeout");
 our $act_timeout = Purple::timeout_add($plugin, $timeout, \&loadinfo_cb, $plugin);
 Purple::Debug::info("lastfm", "Reset timeout to ".$timeout." seconds.\n");
}


sub parse_data_cb {
 my $data = shift;
 Purple::Debug::info("lastfm", "Callback function for HTTP request called.\n");

 my $artist = "";
 my $title     = "";
 my $timestamp = 0;

 if ($data =~ m/<error code="\d*">(.*?)<\/error>/gis) {
  $error = $1;
  Purple::Debug::error("lastfm", "Huh? We got an error message from the server ...\n");
  Purple::Debug::error("lastfm", $error."\n");
  Purple::Debug::error("lastfm", "Skipping status message changing.\n");

  # Reset timeout so refresh is called regularly 
  my $timeout = Purple::Prefs::get_int("/plugins/core/lastfm/timeout");
  our $act_timeout = Purple::timeout_add($plugin, $timeout, \&loadinfo_cb, $plugin);
  Purple::Debug::info("lastfm", "Reset timeout to ".$timeout." seconds.\n");
 } else {
  # Parse string as XML

  my $nowplaying = 0;

  if ($data ne "") {
   my @lines = split(/\n/, $data);
 
   foreach (@lines) {
    my $line = $_;
 
    if ($line =~ /^\s*<artist mbid=.*?>(.*?)<\/artist>$/) {
     $artist = $1;
    } elsif ($line =~ /^\s*<name>(.*?)<\/name>$/) {
     $title = $1;
    } elsif ($line =~ /^\s*<date uts="(\d*)".*$/) {
     $timestamp = $1;
    } elsif ($line =~ /nowplaying="true"/) {
     $nowplaying = 1;
    }
 
    if ($line =~ /^\s*<\/track>$/) {
     last;
    }
   }
  }

  $song = $artist . " - " . $title;

  # Play with the time :)
  my $ago = "";

  if ($nowplaying == 0) {
   my $time = time;
   my $diff = $time - $timestamp;
   Purple::Debug::info("lastfm", "Current time: " . $time . "\n");
   Purple::Debug::info("lastfm", "Srobbled at : " . $timestamp . "\n");
   Purple::Debug::info("lastfm", "Difference  : " . $diff . "\n");

   my $days = int($diff / 86400);
   $diff %= 86400;

   my $hours = int($diff / 3600);
   $diff %= 3600;

   my $minutes = int($diff / 60);

   if ($days > 0) {
    $ago .= $days . " day";

    if ($days > 1) {
     $ago .= "s";
    }
   }

   if ($hours > 0) {
    if ($days > 0) {
     $ago .= ", ";
    }

    $ago .= $hours . " hour";

    if ($hours > 1) {
     $ago .= "s";
    }
   }

   if ($minutes > 0) {
    if (($hours > 0) || ($days > 0)) {
     $ago .= ", ";
    }

    $ago .= $minutes . " minute";

    if ($minutes > 1) {
     $ago .= "s";
    }
   }

   $ago .= " ago";
  } else {
   $ago = "right now";
  }

  $timestring = localtime($timestamp);

  Purple::Debug::info("lastfm", "Song is \"" . $song . "\"\n");
  Purple::Debug::info("lastfm", "Scrobbled " . $ago. ".\n");
  Purple::Debug::info("lastfm", "This was at " . $timestring . ".\n");

  my $nponly = Purple::Prefs::get_bool("/plugins/core/lastfm/nowplayingonly");
  if ($nponly && (! ($nowplaying == 1))) {
   $song = "---";
  }

  # Call method that will hopefully set our new status message
  set_status($song, $ago, $timestring);
 }
}

# Routine that is called in any case that triggers a refresh
sub loadinfo_cb {
 my $plugin = shift;
 Purple::Debug::info("lastfm", "User or timeout requested to refresh info.\n");

 # Find out what we must fetch
 my $url = build_url();

 # Let libpurple do the work
 Purple::Debug::info("lastfm", "Telling libpurple to fetch data.\n");
 Purple::Debug::info("lastfm", "URL to be fetched: " . $url . "\n");
 Purple::Util::fetch_url($plugin, $url, TRUE, "Mozilla/5.0", TRUE, \&parse_data_cb);

 # Stop timeout for now, will be reinstated by set_status
 return false;
}

# Build URL to retrieve from Audioscrobbler
sub build_url {
 my $protocol = "http://";
 my $hostname = "ws.audioscrobbler.com";
 my $username = Purple::Prefs::get_string("/plugins/core/lastfm/username");
 my $content = Purple::Prefs::get_string("/plugins/core/lastfm/content");
 my $uri = "/2.0/?method=" . $content ."&user=". $username . "&api_key=" . $apikey;
 my $url = $protocol . $hostname . $uri;

 return $url;
}