pidgin2adium 3.3.0 → 4.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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