pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Added tag v2.14.3 for changeset 723c284ecfa1
release-2.x.y
2021-04-08, Gary Kramlich
039b66444534
Added tag v2.14.3 for changeset 723c284ecfa1
/*
* 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
"nmrtf.h"
#include
"debug.h"
#include
"util.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
_NMRTFCharProp
{
/* 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
_NMRtfStateSave
{
NMRtfCharProp
chp
;
NMRtfState
rds
;
NMRtfState
ris
;
}
NMRtfStateSave
;
typedef
struct
_NMRtfSymbol
{
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
_NMRtfFont
{
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
;
}
void
nm_rtf_deinit
(
NMRtfContext
*
ctx
)
{
GSList
*
node
;
NMRtfFont
*
font
;
NMRtfStateSave
*
save
;
if
(
ctx
)
{
for
(
node
=
ctx
->
font_table
;
node
;
node
=
node
->
next
)
{
font
=
node
->
data
;
g_free
(
font
->
name
);
g_free
(
font
);
node
->
data
=
NULL
;
}
g_slist_free
(
ctx
->
font_table
);
for
(
node
=
ctx
->
saved
;
node
;
node
=
node
->
next
)
{
save
=
node
->
data
;
g_free
(
save
);
node
->
data
=
NULL
;
}
g_slist_free
(
ctx
->
saved
);
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_remove_link
(
ctx
->
saved
,
link_old
);
g_slist_free_1
(
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_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
;
}