Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwpd/src/lib/WP42Heuristics.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwpd
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2003 William Lachance (wrlach@gmail.com)
11
 * Copyright (C) 2003 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 *
21
 * For further information visit http://libwpd.sourceforge.net
22
 */
23
24
/* "This product is not manufactured, approved, or supported by
25
 * Corel Corporation or Corel Corporation Limited."
26
 */
27
28
#include "WP42Heuristics.h"
29
30
#include <memory>
31
32
#include "WP42FileStructure.h"
33
#include "libwpd_internal.h"
34
35
using namespace libwpd;
36
37
0
WPDPasswordMatch WP42Heuristics::verifyPassword(librevenge::RVNGInputStream *input, const char *password) try
38
0
{
39
0
  if (!password)
40
0
    return WPD_PASSWORD_MATCH_DONTKNOW;
41
42
0
  input->seek(0, librevenge::RVNG_SEEK_SET);
43
44
0
  if (readU8(input, nullptr) == 0xFE && readU8(input, nullptr) == 0xFF &&
45
0
          readU8(input, nullptr) == 0x61 && readU8(input, nullptr) == 0x61)
46
0
  {
47
0
    WPXEncryption encryption(password, 6);
48
0
    if (readU16(input, nullptr) == encryption.getCheckSum())
49
0
      return WPD_PASSWORD_MATCH_OK;
50
0
    else
51
0
      return WPD_PASSWORD_MATCH_NONE;
52
0
  }
53
0
  else
54
0
    return WPD_PASSWORD_MATCH_DONTKNOW;
55
0
}
56
0
catch (...)
57
0
{
58
0
  return WPD_PASSWORD_MATCH_DONTKNOW;
59
0
}
60
61
0
WPDConfidence WP42Heuristics::isWP42FileFormat(librevenge::RVNGInputStream *input, const char *password) try
62
0
{
63
0
  input->seek(0, librevenge::RVNG_SEEK_SET);
64
0
  std::unique_ptr<WPXEncryption> encryption;
65
0
  if (readU8(input, nullptr) == 0xFE && readU8(input, nullptr) == 0xFF &&
66
0
          readU8(input, nullptr) == 0x61 && readU8(input, nullptr) == 0x61)
67
0
  {
68
0
    if (password)
69
0
    {
70
0
      encryption.reset(new WPXEncryption(password, 6));
71
0
      if (readU16(input, nullptr) != encryption->getCheckSum())
72
0
        return WPD_CONFIDENCE_SUPPORTED_ENCRYPTION;
73
0
    }
74
0
    else
75
0
    {
76
0
      if (readU16(input,nullptr) != 0x0000)
77
0
        return WPD_CONFIDENCE_SUPPORTED_ENCRYPTION;
78
0
    }
79
0
  }
80
81
0
  input->seek(0, librevenge::RVNG_SEEK_SET);
82
0
  if (password && encryption)
83
0
    input->seek(6, librevenge::RVNG_SEEK_SET);
84
85
0
  int functionGroupCount = 0;
86
0
  bool invalidUTF8 = false;
87
88
0
  WPD_DEBUG_MSG(("WP42Heuristics::isWP42FileFormat()\n"));
89
90
0
  while (!input->isEnd())
91
0
  {
92
0
    unsigned char readVal = readU8(input, encryption.get());
93
94
0
    WPD_DEBUG_MSG(("WP42Heuristics, Offset 0x%.8x, value 0x%.2x\n", (unsigned int)(input->tell() - 1), readVal));
95
96
0
    if (readVal < (unsigned char)0x20)
97
0
    {
98
      // line breaks et al, skip
99
0
    }
100
0
    else if (readVal >= (unsigned char)0x20 && readVal <= (unsigned char)0x7F)
101
0
    {
102
      // normal ASCII characters, skip
103
0
    }
104
0
    else if (readVal >= (unsigned char)0x80 && readVal <= (unsigned char)0xBF)
105
0
    {
106
      // single character function codes, skip
107
0
      functionGroupCount++;
108
0
    }
109
0
    else if (readVal >= (unsigned char)0xFF)
110
0
    {
111
      // special codes that should not be found as separate functions
112
0
      return WPD_CONFIDENCE_NONE;
113
0
    }
114
0
    else
115
0
    {
116
      // multi character function group
117
118
0
      if (!invalidUTF8 && isValidUtf8(input, encryption.get()))
119
0
      {
120
0
        WPD_DEBUG_MSG(("WP42Heuristics validUTF8\n"));
121
0
        continue;
122
0
      }
123
0
      else
124
0
      {
125
0
        WPD_DEBUG_MSG(("WP42Heuristics invalidUTF8\n"));
126
0
        invalidUTF8 = true;
127
0
      }
128
129
      // check that the size constrains are valid, and that every group_member
130
      // is properly closed at the right place
131
132
0
      if (WP42_FUNCTION_GROUP_SIZE[readVal-0xC0] == -1)
133
0
      {
134
        // variable length function group
135
136
        // skip over all the bytes in the group, and scan for the closing gate
137
0
        unsigned char readNextVal = 0;
138
0
        while (!input->isEnd())
139
0
        {
140
0
          readNextVal = readU8(input, encryption.get());
141
0
          if (readNextVal == readVal)
142
0
            break;
143
0
        }
144
145
        // when passed the complete file, we don't allow for open groups when we've reached EOF
146
0
        if ((readNextVal == 0) || (input->isEnd() && (readNextVal != readVal)))
147
0
          return WPD_CONFIDENCE_NONE;
148
149
0
        functionGroupCount++;
150
0
      }
151
0
      else
152
0
      {
153
        // fixed length function group
154
155
        // seek to the position where the closing gate should be
156
0
        int res = input->seek(WP42_FUNCTION_GROUP_SIZE[readVal-0xC0]-2, librevenge::RVNG_SEEK_CUR);
157
        // when passed the complete file, we should be able to do that
158
0
        if (res)
159
0
          return WPD_CONFIDENCE_NONE;
160
161
        // read the closing gate
162
0
        unsigned char readNextVal = readU8(input, encryption.get());
163
0
        if (readNextVal != readVal)
164
0
          return WPD_CONFIDENCE_NONE;
165
166
0
        functionGroupCount++;
167
0
      }
168
0
    }
169
0
  }
170
171
  /* When we get here, the document is in a format that we *could* import properly.
172
  However, if we didn't entcounter a single WP4.2 function group) we need to be more carefull:
173
  this would be the case when passed a plaintext file for example, which libwpd is not
174
  supposed to handle. */
175
0
  if (!invalidUTF8)
176
0
    return WPD_CONFIDENCE_NONE;
177
0
  if (!functionGroupCount && !encryption)
178
0
    return WPD_CONFIDENCE_NONE;
179
180
0
  return WPD_CONFIDENCE_EXCELLENT;
181
0
}
182
0
catch (...)
183
0
{
184
0
  return WPD_CONFIDENCE_NONE;
185
0
}
186
187
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */