pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Use gUPnP to determine external IP address and control URL
21 months ago, Elliott Sales de Andrade
a5499f6be930
Use gUPnP to determine external IP address and control URL
This is based on the gUPnP example, but modified to use the async API, and its context manager to automatically check all interfaces.
Testing Done:
Opened prefs and checked that external IP was detected with a UPnP-enabled router.
Reviewed at https://reviews.imfreedom.org/r/1785/
/*
* nmrtf.c
*
* Copyright (c) 2004 Novell, Inc. All Rights Reserved.
*
* 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; version 2 of the License.
*
* 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 02111-1301 USA
*
*/
/* This code was adapted from the sample RTF reader found here:
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnrtfspec/html/rtfspec.asp
*/
#include
<glib.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<stddef.h>
#include
<ctype.h>
#include
<string.h>
#include
<purple.h>
#include
"nmrtf.h"
/* Internal RTF parser error codes */
#define NMRTF_OK 0
/* Everything's fine! */
#define NMRTF_STACK_UNDERFLOW 1
/* Unmatched '}' */
#define NMRTF_STACK_OVERFLOW 2
/* Too many '{' -- memory exhausted */
#define NMRTF_UNMATCHED_BRACE 3
/* RTF ended during an open group. */
#define NMRTF_INVALID_HEX 4
/* invalid hex character found in data */
#define NMRTF_BAD_TABLE 5
/* RTF table (sym or prop) invalid */
#define NMRTF_ASSERTION 6
/* Assertion failure */
#define NMRTF_EOF 7
/* End of file reached while reading RTF */
#define NMRTF_CONVERT_ERROR 8
/* Error converting text */
#define NMRTF_MAX_DEPTH 256
typedef
enum
{
NMRTF_STATE_NORMAL
,
NMRTF_STATE_SKIP
,
NMRTF_STATE_FONTTABLE
,
NMRTF_STATE_BIN
,
NMRTF_STATE_HEX
}
NMRtfState
;
/* Rtf State */
/* Property types that we care about */
typedef
enum
{
NMRTF_PROP_FONT_IDX
,
NMRTF_PROP_FONT_CHARSET
,
NMRTF_PROP_MAX
}
NMRtfProperty
;
typedef
enum
{
NMRTF_SPECIAL_BIN
,
NMRTF_SPECIAL_HEX
,
NMRTF_SPECIAL_UNICODE
,
NMRTF_SPECIAL_SKIP
}
NMRtfSpecialKwd
;
typedef
enum
{
NMRTF_DEST_FONTTABLE
,
NMRTF_DEST_SKIP
}
NMRtfDestinationType
;
typedef
enum
{
NMRTF_KWD_CHAR
,
NMRTF_KWD_DEST
,
NMRTF_KWD_PROP
,
NMRTF_KWD_SPEC
}
NMRtfKeywordType
;
typedef
struct
{
/* All we care about for now is the font.
* bold, italic, underline, etc. should be
* added here
*/
int
font_idx
;
int
font_charset
;
}
NMRtfCharProp
;
typedef
struct
{
NMRtfCharProp
chp
;
NMRtfState
rds
;
NMRtfState
ris
;
}
NMRtfStateSave
;
typedef
struct
{
char
*
keyword
;
/* RTF keyword */
int
default_val
;
/* default value to use */
gboolean
pass_default
;
/* true to use default value from this table */
NMRtfKeywordType
kwd_type
;
/* the type of the keyword */
int
action
;
/* property type if the keyword represents a property */
/* destination type if the keyword represents a destination */
/* character to print if the keyword represents a character */
}
NMRtfSymbol
;
typedef
struct
{
int
number
;
char
*
name
;
int
charset
;
}
NMRtfFont
;
/* RTF Context */
struct
_NMRtfContext
{
NMRtfState
rds
;
/* destination state */
NMRtfState
ris
;
/* internal state */
NMRtfCharProp
chp
;
/* current character properties (ie. font, bold, italic, etc.) */
GSList
*
font_table
;
/* the font table */
GSList
*
saved
;
/* saved state stack */
int
param
;
/* numeric parameter for the current keyword */
long
bytes_to_skip
;
/* number of bytes to skip (after encountering \bin) */
int
depth
;
/* how many groups deep are we */
gboolean
skip_unknown
;
/* if true, skip any unknown destinations (this is set after encountering '\*') */
char
*
input
;
/* input string */
guchar
nextch
;
/* next char in input */
gboolean
nextch_available
;
/* nextch value is set */
GString
*
ansi
;
/* Temporary ansi text, will be convert/flushed to the output string */
GString
*
output
;
/* The plain text UTF8 string */
};
static
int
rtf_parse
(
NMRtfContext
*
ctx
);
static
int
rtf_push_state
(
NMRtfContext
*
ctx
);
static
int
rtf_pop_state
(
NMRtfContext
*
ctx
);
static
NMRtfFont
*
rtf_get_font
(
NMRtfContext
*
ctx
,
int
index
);
static
int
rtf_get_char
(
NMRtfContext
*
ctx
,
guchar
*
ch
);
static
int
rtf_unget_char
(
NMRtfContext
*
ctx
,
guchar
ch
);
static
int
rtf_flush_data
(
NMRtfContext
*
ctx
);
static
int
rtf_parse_keyword
(
NMRtfContext
*
ctx
);
static
int
rtf_dispatch_control
(
NMRtfContext
*
ctx
,
char
*
keyword
,
int
param
,
gboolean
param_set
);
static
int
rtf_dispatch_char
(
NMRtfContext
*
ctx
,
guchar
ch
);
static
int
rtf_dispatch_unicode_char
(
NMRtfContext
*
ctx
,
gunichar
ch
);
static
int
rtf_print_char
(
NMRtfContext
*
ctx
,
guchar
ch
);
static
int
rtf_print_unicode_char
(
NMRtfContext
*
ctx
,
gunichar
ch
);
static
int
rtf_change_destination
(
NMRtfContext
*
ctx
,
NMRtfDestinationType
dest
);
static
int
rtf_dispatch_special
(
NMRtfContext
*
ctx
,
NMRtfSpecialKwd
special
);
static
int
rtf_apply_property
(
NMRtfContext
*
ctx
,
NMRtfProperty
prop
,
int
val
);
/* RTF parser tables */
/* Keyword descriptions */
NMRtfSymbol
rtf_symbols
[]
=
{
/* keyword, default, pass_default, keyword_type, action */
{
"fonttbl"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_FONTTABLE
},
{
"f"
,
0
,
FALSE
,
NMRTF_KWD_PROP
,
NMRTF_PROP_FONT_IDX
},
{
"fcharset"
,
0
,
FALSE
,
NMRTF_KWD_PROP
,
NMRTF_PROP_FONT_CHARSET
},
{
"par"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
0x0a
},
{
"line"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
0x0a
},
{
"
\0
x0a"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
0x0a
},
{
"
\0
x0d"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
0x0a
},
{
"tab"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
0x09
},
{
"
\r
"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'\r'
},
{
"
\n
"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'\n'
},
{
"ldblquote"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'"'
},
{
"rdblquote"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'"'
},
{
"{"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'{'
},
{
"}"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'}'
},
{
"
\\
"
,
0
,
FALSE
,
NMRTF_KWD_CHAR
,
'\\'
},
{
"bin"
,
0
,
FALSE
,
NMRTF_KWD_SPEC
,
NMRTF_SPECIAL_BIN
},
{
"*"
,
0
,
FALSE
,
NMRTF_KWD_SPEC
,
NMRTF_SPECIAL_SKIP
},
{
"'"
,
0
,
FALSE
,
NMRTF_KWD_SPEC
,
NMRTF_SPECIAL_HEX
},
{
"u"
,
0
,
FALSE
,
NMRTF_KWD_SPEC
,
NMRTF_SPECIAL_UNICODE
},
{
"colortbl"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"author"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"buptim"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"comment"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"creatim"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"doccomm"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"footer"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"footerf"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"footerl"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"footerr"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"footnote"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"ftncn"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"ftnsep"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"ftnsepc"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"header"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"headerf"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"headerl"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"headerr"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"info"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"keywords"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"operator"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"pict"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"printim"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"private1"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"revtim"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"rxe"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"stylesheet"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"subject"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"tc"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"title"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"txe"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
},
{
"xe"
,
0
,
FALSE
,
NMRTF_KWD_DEST
,
NMRTF_DEST_SKIP
}
};
int
table_size
=
sizeof
(
rtf_symbols
)
/
sizeof
(
NMRtfSymbol
);
NMRtfContext
*
nm_rtf_init
()
{
NMRtfContext
*
ctx
=
g_new0
(
NMRtfContext
,
1
);
ctx
->
nextch_available
=
FALSE
;
ctx
->
ansi
=
g_string_new
(
""
);
ctx
->
output
=
g_string_new
(
""
);
return
ctx
;
}
char
*
nm_rtf_strip_formatting
(
NMRtfContext
*
ctx
,
const
char
*
input
)
{
int
status
;
ctx
->
input
=
(
char
*
)
input
;
status
=
rtf_parse
(
ctx
);
if
(
status
==
NMRTF_OK
)
return
g_strdup
(
ctx
->
output
->
str
);
purple_debug_info
(
"novell"
,
"RTF parser failed with error code %d
\n
"
,
status
);
return
NULL
;
}
static
void
nm_rtf_font_free
(
NMRtfFont
*
font
)
{
g_return_if_fail
(
font
!=
NULL
);
g_free
(
font
->
name
);
g_free
(
font
);
}
void
nm_rtf_deinit
(
NMRtfContext
*
ctx
)
{
if
(
ctx
)
{
g_slist_free_full
(
ctx
->
font_table
,
(
GDestroyNotify
)
nm_rtf_font_free
);
g_slist_free_full
(
ctx
->
saved
,
g_free
);
g_string_free
(
ctx
->
ansi
,
TRUE
);
g_string_free
(
ctx
->
output
,
TRUE
);
g_free
(
ctx
);
}
}
static
const
char
*
get_current_encoding
(
NMRtfContext
*
ctx
)
{
NMRtfFont
*
font
;
font
=
rtf_get_font
(
ctx
,
ctx
->
chp
.
font_idx
);
switch
(
font
->
charset
)
{
case
0
:
return
"CP1252"
;
case
77
:
return
"MACINTOSH"
;
case
78
:
return
"SJIS"
;
case
128
:
return
"CP932"
;
case
129
:
return
"CP949"
;
case
130
:
return
"CP1361"
;
case
134
:
return
"CP936"
;
case
136
:
return
"CP950"
;
case
161
:
return
"CP1253"
;
case
162
:
return
"CP1254"
;
case
163
:
return
"CP1258"
;
case
181
:
case
177
:
return
"CP1255"
;
case
178
:
case
179
:
case
180
:
return
"CP1256"
;
case
186
:
return
"CP1257"
;
case
204
:
return
"CP1251"
;
case
222
:
return
"CP874"
;
case
238
:
return
"CP1250"
;
case
254
:
return
"CP437"
;
default
:
purple_debug_info
(
"novell"
,
"Unhandled font charset %d
\n
"
,
font
->
charset
);
return
"CP1252"
;
}
}
/*
* Add an entry to the font table
*/
static
int
rtf_add_font_entry
(
NMRtfContext
*
ctx
,
int
number
,
const
char
*
name
,
int
charset
)
{
NMRtfFont
*
font
=
g_new0
(
NMRtfFont
,
1
);
font
->
number
=
number
;
font
->
name
=
g_strdup
(
name
);
font
->
charset
=
charset
;
purple_debug_info
(
"novell"
,
"Adding font to table: #%d
\t
%s
\t
%d
\n
"
,
font
->
number
,
font
->
name
,
font
->
charset
);
ctx
->
font_table
=
g_slist_append
(
ctx
->
font_table
,
font
);
return
NMRTF_OK
;
}
/*
* Return the nth entry in the font table
*/
static
NMRtfFont
*
rtf_get_font
(
NMRtfContext
*
ctx
,
int
nth
)
{
NMRtfFont
*
font
;
font
=
g_slist_nth_data
(
ctx
->
font_table
,
nth
);
return
font
;
}
/*
* Step 1:
* Isolate RTF keywords and send them to rtf_parse_keyword;
* Push and pop state at the start and end of RTF groups;
* Send text to rtf_dispatch_char for further processing.
*/
static
int
rtf_parse
(
NMRtfContext
*
ctx
)
{
int
status
;
guchar
ch
;
guchar
hex_byte
=
0
;
int
hex_count
=
2
;
int
len
;
if
(
ctx
->
input
==
NULL
)
return
NMRTF_OK
;
while
(
rtf_get_char
(
ctx
,
&
ch
)
==
NMRTF_OK
)
{
if
(
ctx
->
depth
<
0
)
return
NMRTF_STACK_UNDERFLOW
;
/* if we're parsing binary data, handle it directly */
if
(
ctx
->
ris
==
NMRTF_STATE_BIN
)
{
if
((
status
=
rtf_dispatch_char
(
ctx
,
ch
))
!=
NMRTF_OK
)
return
status
;
}
else
{
switch
(
ch
)
{
case
'{'
:
if
(
ctx
->
depth
>
NMRTF_MAX_DEPTH
)
return
NMRTF_STACK_OVERFLOW
;
rtf_flush_data
(
ctx
);
if
((
status
=
rtf_push_state
(
ctx
))
!=
NMRTF_OK
)
return
status
;
break
;
case
'}'
:
rtf_flush_data
(
ctx
);
/* for some reason there is always an unwanted '\par' at the end */
if
(
ctx
->
rds
==
NMRTF_STATE_NORMAL
)
{
len
=
ctx
->
output
->
len
;
if
(
ctx
->
output
->
str
[
len
-1
]
==
'\n'
)
ctx
->
output
=
g_string_truncate
(
ctx
->
output
,
len
-1
);
}
if
((
status
=
rtf_pop_state
(
ctx
))
!=
NMRTF_OK
)
return
status
;
if
(
ctx
->
depth
<
0
)
return
NMRTF_STACK_OVERFLOW
;
break
;
case
'\\'
:
if
((
status
=
rtf_parse_keyword
(
ctx
))
!=
NMRTF_OK
)
return
status
;
break
;
case
0x0d
:
case
0x0a
:
/* cr and lf are noise characters... */
break
;
default
:
if
(
ctx
->
ris
==
NMRTF_STATE_NORMAL
)
{
if
((
status
=
rtf_dispatch_char
(
ctx
,
ch
))
!=
NMRTF_OK
)
return
status
;
}
else
{
/* parsing a hex encoded character */
if
(
ctx
->
ris
!=
NMRTF_STATE_HEX
)
return
NMRTF_ASSERTION
;
hex_byte
=
hex_byte
<<
4
;
if
(
isdigit
(
ch
))
hex_byte
+=
(
char
)
ch
-
'0'
;
else
{
if
(
islower
(
ch
))
{
if
(
ch
<
'a'
||
ch
>
'f'
)
return
NMRTF_INVALID_HEX
;
hex_byte
+=
(
char
)
ch
-
'a'
+
10
;
}
else
{
if
(
ch
<
'A'
||
ch
>
'F'
)
return
NMRTF_INVALID_HEX
;
hex_byte
+=
(
char
)
ch
-
'A'
+
10
;
}
}
hex_count
--
;
if
(
hex_count
==
0
)
{
if
((
status
=
rtf_dispatch_char
(
ctx
,
hex_byte
))
!=
NMRTF_OK
)
return
status
;
hex_count
=
2
;
hex_byte
=
0
;
ctx
->
ris
=
NMRTF_STATE_NORMAL
;
}
}
break
;
}
}
}
if
(
ctx
->
depth
<
0
)
return
NMRTF_STACK_OVERFLOW
;
if
(
ctx
->
depth
>
0
)
return
NMRTF_UNMATCHED_BRACE
;
return
NMRTF_OK
;
}
/*
* Push the current state onto stack
*/
static
int
rtf_push_state
(
NMRtfContext
*
ctx
)
{
NMRtfStateSave
*
save
=
g_new0
(
NMRtfStateSave
,
1
);
save
->
chp
=
ctx
->
chp
;
save
->
rds
=
ctx
->
rds
;
save
->
ris
=
ctx
->
ris
;
ctx
->
saved
=
g_slist_prepend
(
ctx
->
saved
,
save
);
ctx
->
ris
=
NMRTF_STATE_NORMAL
;
(
ctx
->
depth
)
++
;
return
NMRTF_OK
;
}
/*
* Restore the state at the top of the stack
*/
static
int
rtf_pop_state
(
NMRtfContext
*
ctx
)
{
NMRtfStateSave
*
save_old
;
GSList
*
link_old
;
if
(
ctx
->
saved
==
NULL
)
return
NMRTF_STACK_UNDERFLOW
;
save_old
=
ctx
->
saved
->
data
;
ctx
->
chp
=
save_old
->
chp
;
ctx
->
rds
=
save_old
->
rds
;
ctx
->
ris
=
save_old
->
ris
;
(
ctx
->
depth
)
--
;
g_free
(
save_old
);
link_old
=
ctx
->
saved
;
ctx
->
saved
=
g_slist_delete_link
(
ctx
->
saved
,
link_old
);
return
NMRTF_OK
;
}
/*
* Step 2:
* Get a control word (and its associated value) and
* dispatch the control.
*/
static
int
rtf_parse_keyword
(
NMRtfContext
*
ctx
)
{
int
status
=
NMRTF_OK
;
guchar
ch
;
gboolean
param_set
=
FALSE
;
gboolean
is_neg
=
FALSE
;
int
param
=
0
;
char
keyword
[
30
];
char
parameter
[
20
];
gsize
i
;
keyword
[
0
]
=
'\0'
;
parameter
[
0
]
=
'\0'
;
if
((
status
=
rtf_get_char
(
ctx
,
&
ch
))
!=
NMRTF_OK
)
return
status
;
if
(
!
isalpha
(
ch
))
{
/* a control symbol; no delimiter. */
keyword
[
0
]
=
(
char
)
ch
;
keyword
[
1
]
=
'\0'
;
return
rtf_dispatch_control
(
ctx
,
keyword
,
0
,
param_set
);
}
/* parse keyword */
for
(
i
=
0
;
isalpha
(
ch
)
&&
(
i
<
sizeof
(
keyword
)
-
1
);
rtf_get_char
(
ctx
,
&
ch
))
{
keyword
[
i
]
=
(
char
)
ch
;
i
++
;
}
keyword
[
i
]
=
'\0'
;
/* check for '-' indicated a negative parameter value */
if
(
ch
==
'-'
)
{
is_neg
=
TRUE
;
if
((
status
=
rtf_get_char
(
ctx
,
&
ch
))
!=
NMRTF_OK
)
return
status
;
}
/* check for numerical param */
if
(
isdigit
(
ch
))
{
param_set
=
TRUE
;
for
(
i
=
0
;
isdigit
(
ch
)
&&
(
i
<
sizeof
(
parameter
)
-
1
);
rtf_get_char
(
ctx
,
&
ch
))
{
parameter
[
i
]
=
(
char
)
ch
;
i
++
;
}
parameter
[
i
]
=
'\0'
;
ctx
->
param
=
param
=
atoi
(
parameter
);
if
(
is_neg
)
ctx
->
param
=
param
=
-
param
;
}
/* space after control is optional, put character back if it is not a space */
if
(
ch
!=
' '
)
rtf_unget_char
(
ctx
,
ch
);
return
rtf_dispatch_control
(
ctx
,
keyword
,
param
,
param_set
);
}
/*
* Route the character to the appropriate destination
*/
static
int
rtf_dispatch_char
(
NMRtfContext
*
ctx
,
guchar
ch
)
{
if
(
ctx
->
ris
==
NMRTF_STATE_BIN
&&
--
(
ctx
->
bytes_to_skip
)
<=
0
)
ctx
->
ris
=
NMRTF_STATE_NORMAL
;
switch
(
ctx
->
rds
)
{
case
NMRTF_STATE_SKIP
:
return
NMRTF_OK
;
case
NMRTF_STATE_NORMAL
:
return
rtf_print_char
(
ctx
,
ch
);
case
NMRTF_STATE_FONTTABLE
:
if
(
ch
==
';'
)
{
rtf_add_font_entry
(
ctx
,
ctx
->
chp
.
font_idx
,
ctx
->
ansi
->
str
,
ctx
->
chp
.
font_charset
);
g_string_truncate
(
ctx
->
ansi
,
0
);
}
else
{
return
rtf_print_char
(
ctx
,
ch
);
}
return
NMRTF_OK
;
default
:
return
NMRTF_OK
;
}
}
/* Handle a unicode character */
static
int
rtf_dispatch_unicode_char
(
NMRtfContext
*
ctx
,
gunichar
ch
)
{
switch
(
ctx
->
rds
)
{
case
NMRTF_STATE_SKIP
:
return
NMRTF_OK
;
case
NMRTF_STATE_NORMAL
:
case
NMRTF_STATE_FONTTABLE
:
return
rtf_print_unicode_char
(
ctx
,
ch
);
default
:
return
NMRTF_OK
;
}
}
/*
* Output a character
*/
static
int
rtf_print_char
(
NMRtfContext
*
ctx
,
guchar
ch
)
{
ctx
->
ansi
=
g_string_append_c
(
ctx
->
ansi
,
ch
);
return
NMRTF_OK
;
}
/*
* Output a unicode character
*/
static
int
rtf_print_unicode_char
(
NMRtfContext
*
ctx
,
gunichar
ch
)
{
char
buf
[
7
];
int
num
;
/* convert and flush the ansi buffer to the utf8 buffer */
rtf_flush_data
(
ctx
);
/* convert the unicode character to utf8 and add directly to the output buffer */
num
=
g_unichar_to_utf8
((
gunichar
)
ch
,
buf
);
buf
[
num
]
=
0
;
purple_debug_info
(
"novell"
,
"converted unichar 0x%X to utf8 char %s
\n
"
,
ch
,
buf
);
ctx
->
output
=
g_string_append
(
ctx
->
output
,
buf
);
return
NMRTF_OK
;
}
/*
* Flush the output text
*/
static
int
rtf_flush_data
(
NMRtfContext
*
ctx
)
{
int
status
=
NMRTF_OK
;
char
*
conv_data
=
NULL
;
const
char
*
enc
=
NULL
;
GError
*
gerror
=
NULL
;
if
(
ctx
->
rds
==
NMRTF_STATE_NORMAL
&&
ctx
->
ansi
->
len
>
0
)
{
enc
=
get_current_encoding
(
ctx
);
conv_data
=
g_convert
(
ctx
->
ansi
->
str
,
ctx
->
ansi
->
len
,
"UTF-8"
,
enc
,
NULL
,
NULL
,
&
gerror
);
if
(
conv_data
)
{
ctx
->
output
=
g_string_append
(
ctx
->
output
,
conv_data
);
g_free
(
conv_data
);
ctx
->
ansi
=
g_string_truncate
(
ctx
->
ansi
,
0
);
}
else
{
status
=
NMRTF_CONVERT_ERROR
;
purple_debug_info
(
"novell"
,
"failed to convert data! error code = %d msg = %s
\n
"
,
gerror
->
code
,
gerror
->
message
);
}
}
g_error_free
(
gerror
);
return
status
;
}
/*
* Handle a property change
*/
static
int
rtf_apply_property
(
NMRtfContext
*
ctx
,
NMRtfProperty
prop
,
int
val
)
{
if
(
ctx
->
rds
==
NMRTF_STATE_SKIP
)
/* If we're skipping text, */
return
NMRTF_OK
;
/* don't do anything. */
/* Need to flush any temporary data before a property change*/
rtf_flush_data
(
ctx
);
switch
(
prop
)
{
case
NMRTF_PROP_FONT_IDX
:
ctx
->
chp
.
font_idx
=
val
;
break
;
case
NMRTF_PROP_FONT_CHARSET
:
ctx
->
chp
.
font_charset
=
val
;
break
;
default
:
return
NMRTF_BAD_TABLE
;
}
return
NMRTF_OK
;
}
/*
* Step 3.
* Search the table for keyword and evaluate it appropriately.
*
* Inputs:
* keyword: The RTF control to evaluate.
* param: The parameter of the RTF control.
* param_set: TRUE if the control had a parameter; (that is, if param is valid)
* FALSE if it did not.
*/
static
int
rtf_dispatch_control
(
NMRtfContext
*
ctx
,
char
*
keyword
,
int
param
,
gboolean
param_set
)
{
int
idx
;
for
(
idx
=
0
;
idx
<
table_size
;
idx
++
)
{
if
(
purple_strequal
(
keyword
,
rtf_symbols
[
idx
].
keyword
))
break
;
}
if
(
idx
==
table_size
)
{
if
(
ctx
->
skip_unknown
)
ctx
->
rds
=
NMRTF_STATE_SKIP
;
ctx
->
skip_unknown
=
FALSE
;
return
NMRTF_OK
;
}
/* found it! use kwd_type and action to determine what to do with it. */
ctx
->
skip_unknown
=
FALSE
;
switch
(
rtf_symbols
[
idx
].
kwd_type
)
{
case
NMRTF_KWD_PROP
:
if
(
rtf_symbols
[
idx
].
pass_default
||
!
param_set
)
param
=
rtf_symbols
[
idx
].
default_val
;
return
rtf_apply_property
(
ctx
,
rtf_symbols
[
idx
].
action
,
param
);
case
NMRTF_KWD_CHAR
:
return
rtf_dispatch_char
(
ctx
,
rtf_symbols
[
idx
].
action
);
case
NMRTF_KWD_DEST
:
return
rtf_change_destination
(
ctx
,
rtf_symbols
[
idx
].
action
);
case
NMRTF_KWD_SPEC
:
return
rtf_dispatch_special
(
ctx
,
rtf_symbols
[
idx
].
action
);
default
:
return
NMRTF_BAD_TABLE
;
}
return
NMRTF_BAD_TABLE
;
}
/*
* Change to the destination specified.
*/
static
int
rtf_change_destination
(
NMRtfContext
*
ctx
,
NMRtfDestinationType
type
)
{
/* if we're skipping text, don't do anything */
if
(
ctx
->
rds
==
NMRTF_STATE_SKIP
)
return
NMRTF_OK
;
switch
(
type
)
{
case
NMRTF_DEST_FONTTABLE
:
ctx
->
rds
=
NMRTF_STATE_FONTTABLE
;
g_string_truncate
(
ctx
->
ansi
,
0
);
break
;
default
:
ctx
->
rds
=
NMRTF_STATE_SKIP
;
/* when in doubt, skip it... */
break
;
}
return
NMRTF_OK
;
}
/*
* Dispatch an RTF control that needs special processing
*/
static
int
rtf_dispatch_special
(
NMRtfContext
*
ctx
,
NMRtfSpecialKwd
type
)
{
int
status
=
NMRTF_OK
;
guchar
ch
;
if
(
ctx
->
rds
==
NMRTF_STATE_SKIP
&&
type
!=
NMRTF_SPECIAL_BIN
)
/* if we're skipping, and it's not */
return
NMRTF_OK
;
/* the \bin keyword, ignore it. */
switch
(
type
)
{
case
NMRTF_SPECIAL_BIN
:
ctx
->
ris
=
NMRTF_STATE_BIN
;
ctx
->
bytes_to_skip
=
ctx
->
param
;
break
;
case
NMRTF_SPECIAL_SKIP
:
ctx
->
skip_unknown
=
TRUE
;
break
;
case
NMRTF_SPECIAL_HEX
:
ctx
->
ris
=
NMRTF_STATE_HEX
;
break
;
case
NMRTF_SPECIAL_UNICODE
:
purple_debug_info
(
"novell"
,
"parsing unichar
\n
"
);
status
=
rtf_dispatch_unicode_char
(
ctx
,
ctx
->
param
);
/* Skip next char */
if
(
status
==
NMRTF_OK
)
status
=
rtf_get_char
(
ctx
,
&
ch
);
break
;
default
:
status
=
NMRTF_BAD_TABLE
;
break
;
}
return
status
;
}
/*
* Get the next character from the input stream
*/
static
int
rtf_get_char
(
NMRtfContext
*
ctx
,
guchar
*
ch
)
{
if
(
ctx
->
nextch_available
)
{
*
ch
=
ctx
->
nextch
;
ctx
->
nextch_available
=
FALSE
;
}
else
{
*
ch
=
*
(
ctx
->
input
);
ctx
->
input
++
;
}
if
(
*
ch
)
return
NMRTF_OK
;
else
return
NMRTF_EOF
;
}
/*
* Move a character back into the input stream
*/
static
int
rtf_unget_char
(
NMRtfContext
*
ctx
,
guchar
ch
)
{
ctx
->
nextch
=
ch
;
ctx
->
nextch_available
=
TRUE
;
return
NMRTF_OK
;
}