pidgin2adium 3.3.0 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -3
  3. data/.rspec +1 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +12 -0
  6. data/Gemfile +6 -1
  7. data/LICENSE +17 -17
  8. data/NEWS.md +89 -0
  9. data/README.md +60 -0
  10. data/Rakefile +5 -23
  11. data/bin/pidgin2adium +19 -90
  12. data/lib/pidgin2adium.rb +4 -136
  13. data/lib/pidgin2adium/adium_chat_file_creator.rb +64 -0
  14. data/lib/pidgin2adium/file_finder.rb +23 -0
  15. data/lib/pidgin2adium/runner.rb +23 -0
  16. data/lib/pidgin2adium/version.rb +1 -1
  17. data/pidgin2adium.gemspec +25 -21
  18. data/spec/features/parse_pidgin_log_file_spec.rb +50 -0
  19. data/spec/fixtures/input/input.html +3 -0
  20. data/spec/fixtures/output.xml +5 -0
  21. data/spec/pidgin2adium/adium_chat_file_creator_spec.rb +89 -0
  22. data/spec/pidgin2adium/file_finder_spec.rb +63 -0
  23. data/spec/spec_helper.rb +17 -59
  24. metadata +96 -89
  25. data/.autotest +0 -28
  26. data/ChangeLog +0 -79
  27. data/Manifest.txt +0 -18
  28. data/README.rdoc +0 -122
  29. data/config/website.yml +0 -2
  30. data/ext/balance_tags_c/balance_tags_c.c +0 -198
  31. data/ext/balance_tags_c/extconf.rb +0 -4
  32. data/lib/pidgin2adium/log_converter.rb +0 -71
  33. data/lib/pidgin2adium/log_file.rb +0 -100
  34. data/lib/pidgin2adium/log_parser.rb +0 -2
  35. data/lib/pidgin2adium/message.rb +0 -2
  36. data/lib/pidgin2adium/messages/all.rb +0 -5
  37. data/lib/pidgin2adium/messages/auto_reply_message.rb +0 -11
  38. data/lib/pidgin2adium/messages/event.rb +0 -17
  39. data/lib/pidgin2adium/messages/message.rb +0 -39
  40. data/lib/pidgin2adium/messages/status_message.rb +0 -17
  41. data/lib/pidgin2adium/messages/xml_message.rb +0 -40
  42. data/lib/pidgin2adium/parsers/all.rb +0 -3
  43. data/lib/pidgin2adium/parsers/basic_parser.rb +0 -456
  44. data/lib/pidgin2adium/parsers/html_log_parser.rb +0 -125
  45. data/lib/pidgin2adium/parsers/text_log_parser.rb +0 -39
  46. data/spec/balance_tags_c_extn_spec.rb +0 -47
  47. data/spec/basic_parser_spec.rb +0 -219
  48. data/spec/html_log_parser_spec.rb +0 -150
  49. data/spec/log_converter_spec.rb +0 -48
  50. data/spec/log_file_spec.rb +0 -176
  51. data/spec/logfiles/2006-12-21.223606.txt +0 -3
  52. data/spec/logfiles/2008-01-15.071445-0500PST.htm +0 -5
  53. data/spec/logfiles/2008-01-15.071445-0500PST.html +0 -5
  54. data/spec/pidgin2adium_spec.rb +0 -252
  55. data/spec/spec.opts +0 -1
  56. data/spec/test-output/README.md +0 -1
  57. data/spec/test-output/html_log_output.xml +0 -6
  58. data/spec/test-output/text_log_output.xml +0 -4
  59. data/spec/text_log_parser_spec.rb +0 -42
  60. data/tasks/extconf.rake +0 -8
  61. data/tasks/extconf/balance_tags_c.rake +0 -47
data/config/website.yml DELETED
@@ -1,2 +0,0 @@
1
- host: gbw@rubyforge.org
2
- remote_dir: /var/www/gforge-projects/pidgin2adium/
@@ -1,198 +0,0 @@
1
- /*
2
- * Balances tags of string using a modified stack. Returns a balanced string.
3
- *
4
- * From Wordpress's formatting.php and rewritten in C by
5
- * Gabe Berke-Williams, 2010.
6
- *
7
- * Original Author:: Leonard Lin <leonard@acm.org>
8
- * License:: GPL v2.0
9
- * Copyright:: November 4, 2001
10
- */
11
-
12
- #include <ruby.h>
13
-
14
- #ifndef RARRAY_PTR
15
- #define RARRAY_PTR(arr) RARRAY(arr)->ptr
16
- #endif
17
-
18
- #ifndef RSTRING_LEN
19
- #define RSTRING_LEN(str) RSTRING(str)->len
20
- #endif
21
-
22
- VALUE balance_tags_c(VALUE, VALUE);
23
- static VALUE mP2A;
24
-
25
- /*
26
- * call-seq: Pidgin2Adium.balance_tags_c(text) => text with balanced HTML tags
27
- *
28
- * Balances tags of _text_. Returns modified text.
29
- */
30
- VALUE balance_tags_c(VALUE mod, VALUE text){
31
- if( TYPE(text) != T_STRING ){
32
- rb_raise(rb_eArgError, "bad argument to balance_tags_c, String only please.");
33
- }
34
- VALUE tagstack = rb_ary_new2(1);
35
- int stacksize = 0;
36
- VALUE tagqueue = rb_str_new2("");
37
- VALUE ZERO = INT2FIX(0),
38
- ONE = INT2FIX(1);
39
- VALUE newtext = rb_str_new2("");
40
- // Known single-entity/self-closing tags
41
- VALUE single_tags = rb_ary_new3(5,
42
- rb_str_new2("br"),
43
- rb_str_new2("hr"),
44
- rb_str_new2("img"),
45
- rb_str_new2("input"),
46
- rb_str_new2("meta"));
47
- // Tags that can be immediately nested within themselves
48
- VALUE nestable_tags = rb_ary_new3(4,
49
- rb_str_new2("blockquote"),
50
- rb_str_new2("div"),
51
- rb_str_new2("span"),
52
- rb_str_new2("font"));
53
- // 1: tagname, with possible leading "/"
54
- // 2: attributes
55
- VALUE tag_regex = rb_eval_string("/<(\\/?\\w*)\\s*([^>]*)>/");
56
- VALUE pos; // position in text
57
- VALUE match;
58
- VALUE tag;
59
- VALUE attributes;
60
- VALUE t; // loop variable when iterating over tagstack at end of while loop
61
- int matchlen;
62
- int done = 0;
63
- int j, k, i; // loop counters
64
-
65
- // WP bug fix for comments - in case you REALLY meant to type '< !--'
66
- rb_funcall(text, rb_intern("gsub!"), 2,
67
- rb_str_new2("< !--"),
68
- rb_str_new2("< !--"));
69
-
70
- // WP bug fix for LOVE <3 (and other situations with '<' before a number)
71
- rb_funcall(text,rb_intern("gsub!"), 2,
72
- rb_eval_string("/<([0-9]{1})/"),
73
- rb_str_new2("&lt;\\1"));
74
-
75
- pos = rb_funcall(text, rb_intern("=~"), 1, tag_regex);
76
- done = (pos == Qnil);
77
- while ( ! done ){
78
- rb_str_concat(newtext, tagqueue);
79
- match = rb_funcall(text, rb_intern("match"), 1, tag_regex);
80
- tag = rb_funcall(rb_reg_nth_match(1, match), rb_intern("downcase"), 0);
81
- attributes = rb_reg_nth_match(2, match);
82
-
83
- matchlen = NUM2INT(rb_funcall(rb_reg_nth_match(0, match), rb_intern("size"), 0));
84
-
85
- // clear the shifter
86
- tagqueue = rb_str_new2("");
87
- // Pop or Push
88
- if (0 == rb_str_cmp(rb_str_substr(tag, 0, 1), rb_str_new2("/"))){ // End Tag
89
- rb_funcall(tag, rb_intern("slice!"), 2, ZERO, ONE);
90
- // if too many closing tags
91
- if(stacksize <= 0){
92
- tag = rb_str_new2("");
93
- //or close to be safe: tag = '/' << tag
94
- } else if (0 == rb_str_cmp(RARRAY_PTR(tagstack)[stacksize - 1], tag)){
95
- // found closing tag
96
- // if stacktop value == tag close value then pop
97
- // Close Tag
98
- tag = rb_str_append(rb_str_new2("</"), tag);
99
- rb_str_concat(tag, rb_str_new2(">"));
100
- // Pop
101
- rb_ary_pop(tagstack);
102
- stacksize--;
103
- } else { // closing tag not at top, search for it
104
- for(j=stacksize-1; j>=0; j--){
105
- if(0 == rb_str_cmp(RARRAY_PTR(tagstack)[j], tag) ){
106
- // add tag to tagqueue
107
- for(k = stacksize-1;k>=j;k--){
108
- rb_str_concat(tagqueue, rb_str_new2("</"));
109
- rb_str_concat(tagqueue, rb_ary_pop(tagstack));
110
- rb_str_concat(tagqueue, rb_str_new2(">"));
111
- stacksize--;
112
- }
113
- break;
114
- }
115
- }
116
- tag = rb_str_new2("");
117
- }
118
- } else {
119
- // Begin Tag
120
-
121
- // Tag Cleaning
122
-
123
- if( ( RSTRING_LEN(attributes) > 0 && // test length before rb_str_substr
124
- (0 == rb_str_cmp(rb_str_substr(attributes, -1, 1), rb_str_new2("/"))) ) ||
125
- (0 == rb_str_cmp(tag, rb_str_new2(""))) ){
126
- // If: self-closing or '', don't do anything.
127
- } else if ( rb_ary_includes(single_tags, tag) ) {
128
- // ElseIf: it's a known single-entity tag but it doesn't close itself, do so
129
- rb_str_concat(attributes, rb_str_new2("/"));
130
- } else {
131
- // Push the tag onto the stack
132
- // If the top of the stack is the same as the tag we want
133
- // to push, close previous tag
134
- if ( (stacksize > 0) &&
135
- (Qfalse == rb_ary_includes(nestable_tags, tag)) &&
136
- (0 == rb_str_cmp(rb_ary_entry(tagstack, stacksize - 1), tag))){
137
- tagqueue = rb_str_new2("</");
138
- rb_str_concat(tagqueue, rb_ary_pop(tagstack));
139
- rb_str_concat(tagqueue, rb_str_new2(">"));
140
- stacksize--;
141
- }
142
- rb_ary_push(tagstack, tag);
143
- stacksize++;
144
- }
145
-
146
- // Attributes
147
- if( 0 != rb_str_cmp(attributes, rb_str_new2("")) ){
148
- attributes = rb_str_plus(rb_str_new2(" "), attributes);
149
- }
150
- tag = rb_str_plus(rb_str_new2("<"), tag);
151
- rb_str_concat(tag, attributes);
152
- rb_str_concat(tag, rb_str_new2(">"));
153
- //If already queuing a close tag, then put this tag on, too
154
- if( RSTRING_LEN(tagqueue) > 0 ){
155
- rb_str_concat(tagqueue, tag);
156
- tag = rb_str_new2("");
157
- }
158
- }
159
- rb_str_concat(newtext,
160
- rb_str_plus(rb_str_substr(text, 0, NUM2INT(pos)), tag));
161
- text = rb_str_substr(text,
162
- NUM2INT(pos)+matchlen,
163
- RSTRING_LEN(text) - (NUM2INT(pos)+matchlen));
164
- pos = rb_funcall(text, rb_intern("=~"), 1, tag_regex);
165
- done = (pos == Qnil);
166
- }
167
-
168
- // Clear Tag Queue
169
- rb_str_concat(newtext, tagqueue);
170
-
171
- // Add Remaining text
172
- rb_str_concat(newtext, text);
173
-
174
- i = NUM2INT(rb_funcall(tagstack, rb_intern("length"), 0)) - 1;
175
- // Empty Stack
176
- for(; i >= 0; i--){
177
- // Add remaining tags to close
178
- t = RARRAY_PTR(tagstack)[i];
179
- rb_str_concat(newtext, rb_str_new2("</"));
180
- rb_str_concat(newtext, t);
181
- rb_str_concat(newtext, rb_str_new2(">"));
182
- }
183
-
184
- // WP fix for the bug with HTML comments
185
- rb_funcall(newtext, rb_intern("gsub!"), 2,
186
- rb_str_new2("< !--"),
187
- rb_str_new2("<!--"));
188
- rb_funcall(newtext, rb_intern("gsub!"), 2,
189
- rb_str_new2("< !--"),
190
- rb_str_new2("< !--"));
191
-
192
- return newtext;
193
- }
194
-
195
- void Init_balance_tags_c(){
196
- mP2A = rb_define_module("Pidgin2Adium");
197
- rb_define_module_function(mP2A, "balance_tags_c", balance_tags_c, 1);
198
- }
@@ -1,4 +0,0 @@
1
- require 'mkmf'
2
-
3
- dir_config("balance_tags_c")
4
- create_makefile("balance_tags_c")
@@ -1,71 +0,0 @@
1
- require 'pidgin2adium'
2
-
3
- module Pidgin2Adium
4
- # An easy way to batch-process a directory. Used by the pidgin2adium
5
- # command-line script.
6
- class LogConverter
7
- include Pidgin2Adium
8
- # You can add options using the _opts_ hash, which can have the
9
- # following keys, all of which are optional:
10
- # * *overwrite*: If true, then overwrite even if log is found.
11
- # Defaults to false.
12
- # * *output_dir*: The top-level dir to put the logs in.
13
- # Logs under output_dir are still each in their own folders, etc.
14
- # Defaults to Pidgin2Adium::ADIUM_LOG_DIR
15
- def initialize(pidgin_log_dir, aliases, opts = {})
16
- # parse_and_generate will process it for us
17
- @opts = opts
18
-
19
- @pidgin_log_dir = File.expand_path(pidgin_log_dir)
20
- @my_aliases = aliases
21
-
22
- unless File.directory?(@pidgin_log_dir)
23
- msg = "Source directory #{@pidgin_log_dir} does not exist or is not a directory."
24
- error(msg)
25
-
26
- # ENOENT automatically prepends "No such file or directory - " to
27
- # its initializer's arguments
28
- raise Errno::ENOENT.new("source directory #{@pidgin_log_dir}")
29
- end
30
- end
31
-
32
- # Runs Pidgin2Adium::parse_and_generate on every log file in directory
33
- # provided in new, then deletes Adium's search indexes to force
34
- # it to rescan logs on startup.
35
- def start
36
- log_msg "Begin converting."
37
- begin
38
- files_path = get_all_chat_files()
39
- rescue Errno::EACCES => bang
40
- error("Sorry, permission denied for getting Pidgin chat files from #{@pidgin_log_dir}.")
41
- error("Details: #{bang.message}")
42
- raise bang
43
- end
44
-
45
- total_files = files_path.size
46
- total_successes = 0
47
- log_msg("#{total_files} files to convert.")
48
- files_path.each_with_index do |fname, i|
49
- log_msg(
50
- sprintf("[%d/%d] Converting %s...",
51
- (i+1), total_files, fname)
52
- )
53
- result = parse_and_generate(fname, @my_aliases, @opts)
54
- total_successes += 1 if result == true
55
- end
56
-
57
- delete_search_indexes()
58
-
59
- Pidgin2Adium.log_msg "Finished converting! Converted #{total_successes} files of #{total_files} total."
60
- puts "Minor error messages:"
61
- puts @@oops_messages.join("\n")
62
- puts "Major error messages:"
63
- puts @@error_messages.join("\n")
64
- end
65
-
66
- def get_all_chat_files
67
- # recurse into each subdir
68
- Dir.glob("#{@pidgin_log_dir}/**/*.{htm,html,txt}") - BAD_DIRS
69
- end
70
- end
71
- end
@@ -1,100 +0,0 @@
1
- require 'fileutils'
2
-
3
- module Pidgin2Adium
4
- # A holding object for the result of LogParser.parse. It makes the
5
- # instance variable @chat_lines available, which is an array of Message
6
- # subclass instances (XMLMessage, Event, etc.)
7
- # Here is a list of the instance variables for each class in @chat_lines:
8
- #
9
- # <b>All of these variables are read/write.</b>
10
- # All:: sender, time, buddy_alias
11
- # XMLMessage:: body
12
- # AutoReplyMessage:: body
13
- # Event:: body, event_type
14
- # StatusMessage:: status
15
- class LogFile
16
- include Pidgin2Adium
17
- include Enumerable
18
- def initialize(chat_lines, service, user_SN, partner_SN, adium_chat_time_start)
19
- @chat_lines = chat_lines
20
- @user_SN = user_SN
21
- @partner_SN = partner_SN
22
- @adium_chat_time_start = adium_chat_time_start
23
-
24
- # @chat_str is generated when to_s is called
25
- @chat_str = nil
26
-
27
- # key is for Pidgin, value is for Adium
28
- # Just used for <service>.<screenname> in directory structure
29
- service_name_map = {'aim' => 'AIM',
30
- 'jabber' =>'Jabber',
31
- 'gtalk'=> 'GTalk',
32
- 'icq' => 'ICQ',
33
- 'qq' => 'QQ',
34
- 'msn' => 'MSN',
35
- 'yahoo' => 'Yahoo!'}
36
-
37
- @service = service_name_map[service.downcase]
38
- end
39
-
40
- attr_reader :chat_lines, :service, :user_SN, :partner_SN, :adium_chat_time_start
41
-
42
- # Returns contents of log file
43
- def to_s
44
- # Faster than inject() or each()
45
- @chat_str ||= @chat_lines.map{|l| l.to_s }.join
46
- end
47
-
48
- def each(&blk)
49
- @chat_lines.each{|l| yield l }
50
- end
51
-
52
- # Set overwrite=true to create a logfile even if logfile already exists.
53
- # Returns one of:
54
- # * false (if an error occurred),
55
- # * Pidgin2Adium::FILE_EXISTS if the file to be generated already exists and overwrite=false, or
56
- # * the path to the new Adium log file.
57
- def write_out(overwrite = false, output_dir_base = ADIUM_LOG_DIR)
58
- # output_dir_base + "/buddyname (2009-08-04T18.38.50-0700).chatlog"
59
- output_dir = File.join(output_dir_base, "#{@service}.#{@user_SN}", @partner_SN, "#{@partner_SN} (#{@adium_chat_time_start}).chatlog")
60
- # output_dir + "/buddyname (2009-08-04T18.38.50-0700).chatlog/buddyname (2009-08-04T18.38.50-0700).xml"
61
- output_path = output_dir + '/' + "#{@partner_SN} (#{@adium_chat_time_start}).xml"
62
- begin
63
- FileUtils.mkdir_p(output_dir)
64
- rescue => bang
65
- error "Could not create destination directory for log file. (Details: #{bang.class}: #{bang.message})"
66
- return false
67
- end
68
- if overwrite
69
- unless File.exist?(output_path)
70
- # File doesn't exist, but maybe it does with a different
71
- # time zone. Check for a file that differs only in time
72
- # zone and, if found, change @output_path to target it.
73
- maybe_matches = Dir.glob(output_dir_base + '/' << File.basename(output_path).sub(/-\d{4}\)\.chatlog$/, '') << '/*')
74
- unless maybe_matches.empty?
75
- output_path = maybe_matches[0]
76
- end
77
- end
78
- else
79
- if File.exist?(output_path)
80
- return FILE_EXISTS
81
- end
82
- end
83
-
84
- begin
85
- outfile = File.new(output_path, 'w')
86
- rescue => bang
87
- error "Could not open log file for writing. (Details: #{bang.class}: #{bang.message})"
88
- return false
89
- end
90
-
91
- # no \n before </chat> because @chat_str (from to_s) has it already
92
- outfile.printf('<?xml version="1.0" encoding="UTF-8" ?>'<<"\n"+
93
- '<chat xmlns="http://purl.org/net/ulf/ns/0.4-02" account="%s" service="%s">'<<"\n"<<'%s</chat>',
94
- @user_SN, @service, self.to_s)
95
- outfile.close
96
-
97
- return output_path
98
- end
99
- end
100
- end
@@ -1,2 +0,0 @@
1
- # For backwards compatibility with Pidgin2Adium < 3.1.1
2
- require 'pidgin2adium/parsers/all'
@@ -1,2 +0,0 @@
1
- # For backwards compatibility with Pidgin2Adium < 3.1.1
2
- require 'pidgin2adium/message/all'
@@ -1,5 +0,0 @@
1
- require 'pidgin2adium/messages/message.rb'
2
- require 'pidgin2adium/messages/xml_message.rb'
3
- require 'pidgin2adium/messages/auto_reply_message.rb'
4
- require 'pidgin2adium/messages/event.rb'
5
- require 'pidgin2adium/messages/status_message.rb'
@@ -1,11 +0,0 @@
1
- # The Message class's subclasses, each used for holding one line of a chat.
2
-
3
- module Pidgin2Adium
4
- # An auto reply message.
5
- class AutoReplyMessage < XMLMessage
6
- def to_s
7
- return sprintf('<message sender="%s" time="%s" auto="true" alias="%s">%s</message>' << "\n",
8
- @sender, @time, @buddy_alias, @styled_body)
9
- end
10
- end
11
- end
@@ -1,17 +0,0 @@
1
- module Pidgin2Adium
2
- # Pidgin does not have Events, but Adium does. Pidgin mostly uses system
3
- # messages to display what Adium calls events. These include sending a file,
4
- # starting a Direct IM connection, or an error in chat.
5
- class Event < XMLMessage
6
- def initialize(sender, time, buddy_alias, body, event_type)
7
- super(sender, time, buddy_alias, body)
8
- @event_type = event_type
9
- end
10
- attr_accessor :event_type
11
-
12
- def to_s
13
- return sprintf('<event type="%s" sender="%s" time="%s" alias="%s">%s</event>',
14
- @event_type, @sender, @time, @buddy_alias, @styled_body)
15
- end
16
- end
17
- end
@@ -1,39 +0,0 @@
1
- # The Message class and its subclasses, each used for holding one line of a
2
- # chat.
3
-
4
- module Pidgin2Adium
5
- # A holding object for each line of the chat. It is subclassed as
6
- # appropriate (eg AutoReplyMessage). Each subclass (but not Message
7
- # itself) has its own to_s which prints out its information in a format
8
- # appropriate for putting in an Adium log file.
9
- # Subclasses: XMLMessage, AutoReplyMessage, StatusMessage, Event.
10
- class Message
11
- include Comparable
12
- def initialize(sender, time, buddy_alias)
13
- # The sender's screen name
14
- @sender = sender
15
- # The time the message was sent, in Adium format (e.g.
16
- # "2008-10-05T22:26:20-0800")
17
- @time = time
18
- @time_object = Time.parse(@time)
19
- # The receiver's alias (NOT screen name)
20
- @buddy_alias = buddy_alias
21
- end
22
- attr_accessor :sender, :time, :buddy_alias
23
-
24
- # Compare this Message to +other_message+, based on their timestamps.
25
- # Returns a number < 0 if this message was sent before +other_message+,
26
- # 0 if they were sent at the same time, and a number > 0 if this message
27
- # was sent after +other_message+.
28
- def <=>(other_message)
29
- return @time_object - other_message.time_object
30
- end
31
-
32
- protected
33
- # Protected because Time.parse doesn't have exactly the right time
34
- # zone. It works fine for <=>, though.
35
- def time_object
36
- return @time_object
37
- end
38
- end
39
- end